Dan Ballard pushed to branch tor-browser-128.8.0esr-14.5-1 at The Tor Project / Applications / Tor Browser

Commits:

12 changed files:

Changes:

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
    ... ... @@ -1111,6 +1111,7 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorAn
    1111 1111
                     .setAllCapsForActionButton(false)
    
    1112 1112
                     .setAction(getString(R.string.connection_assist_connect_to_tor_before_opening_links_confirmation)) {
    
    1113 1113
                         urlQuickLoadViewModel.urlToLoadAfterConnecting.value = searchTermOrURL
    
    1114
    +                    urlQuickLoadViewModel.maybeBeginBootstrap()
    
    1114 1115
                         if (navHost.navController.previousBackStackEntry?.destination?.id == R.id.torConnectionAssistFragment) {
    
    1115 1116
                             supportFragmentManager.popBackStack()
    
    1116 1117
                         } else {
    

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/ConnectAssistUiState.kt
    ... ... @@ -2,14 +2,12 @@ package org.mozilla.fenix.tor
    2 2
     
    
    3 3
     import androidx.annotation.ColorRes
    
    4 4
     import androidx.annotation.DrawableRes
    
    5
    -import androidx.annotation.IntRange
    
    6 5
     import androidx.annotation.StringRes
    
    7 6
     import org.mozilla.fenix.R
    
    8 7
     
    
    9 8
     enum class ConnectAssistUiState(
    
    10 9
         val progressBarVisible: Boolean,
    
    11
    -    @IntRange(0, 100) var progress: Int = 0,
    
    12
    -    @ColorRes val progressTintColorResource: Int? = null,
    
    10
    +    @ColorRes val progressBackgroundTintColorResource: Int = R.color.progress_background_tint,
    
    13 11
         val backButtonVisible: Boolean,
    
    14 12
         val settingsButtonVisible: Boolean,
    
    15 13
         val torConnectImageVisible: Boolean,
    
    ... ... @@ -23,18 +21,19 @@ enum class ConnectAssistUiState(
    23 21
         @StringRes val internetErrorDescription2: Int? = null,
    
    24 22
         @StringRes val titleDescriptionTextStringResource: Int? = R.string.preferences_tor_network_settings_explanation,
    
    25 23
         val quickstartSwitchVisible: Boolean,
    
    26
    -    val unblockTheInternetInCountryDescriptionVisible: Boolean,
    
    27 24
         val countryDropDownVisible: Boolean,
    
    25
    +    @StringRes val countryDropDownDefaultItem: Int = R.string.connection_assist_automatic_country_detection,
    
    28 26
         val torBootstrapButton1Visible: Boolean,
    
    29 27
         @StringRes val torBootstrapButton1TextStringResource: Int = R.string.tor_bootstrap_connect,
    
    30
    -    val torBootstrapButton1ShouldShowTryingABridge: Boolean = false,
    
    28
    +    val torBootstrapButton1ShouldTryABridge: Boolean = false,
    
    29
    +    val torBootstrapButton1ShouldOpenSettings: Boolean = false,
    
    31 30
         val torBootstrapButton2Visible: Boolean,
    
    32 31
         @StringRes val torBootstrapButton2TextStringResource: Int? = R.string.connection_assist_configure_connection_button,
    
    33 32
         val torBootstrapButton2ShouldOpenSettings: Boolean = true,
    
    34 33
         val wordmarkLogoVisible: Boolean = false,
    
    35 34
         val torBootstrapButton2ShouldRestartApp: Boolean = false,
    
    36 35
     ) {
    
    37
    -    Splash(
    
    36
    +    Loading(
    
    38 37
             progressBarVisible = false,
    
    39 38
             backButtonVisible = false,
    
    40 39
             settingsButtonVisible = false,
    
    ... ... @@ -42,15 +41,13 @@ enum class ConnectAssistUiState(
    42 41
             titleLargeTextViewVisible = false,
    
    43 42
             titleDescriptionVisible = false,
    
    44 43
             quickstartSwitchVisible = false,
    
    45
    -        unblockTheInternetInCountryDescriptionVisible = false,
    
    46 44
             countryDropDownVisible = false,
    
    47 45
             torBootstrapButton1Visible = false,
    
    48 46
             torBootstrapButton2Visible = false,
    
    49 47
             wordmarkLogoVisible = true,
    
    50 48
         ),
    
    51
    -    Configuring(
    
    49
    +    Start(
    
    52 50
             progressBarVisible = false,
    
    53
    -        progress = 0,
    
    54 51
             backButtonVisible = false,
    
    55 52
             settingsButtonVisible = true,
    
    56 53
             torConnectImageVisible = true,
    
    ... ... @@ -60,16 +57,14 @@ enum class ConnectAssistUiState(
    60 57
             titleDescriptionVisible = true,
    
    61 58
             titleDescriptionTextStringResource = R.string.preferences_tor_network_settings_explanation,
    
    62 59
             quickstartSwitchVisible = true,
    
    63
    -        unblockTheInternetInCountryDescriptionVisible = false,
    
    64 60
             countryDropDownVisible = false,
    
    65 61
             torBootstrapButton1Visible = true,
    
    66 62
             torBootstrapButton2Visible = true,
    
    67 63
             torBootstrapButton2TextStringResource = R.string.connection_assist_configure_connection_button,
    
    68 64
             torBootstrapButton2ShouldOpenSettings = true,
    
    69 65
         ),
    
    70
    -    Connecting(
    
    66
    +    Bootstrapping(
    
    71 67
             progressBarVisible = true,
    
    72
    -        progress = 0,
    
    73 68
             backButtonVisible = false,
    
    74 69
             settingsButtonVisible = true,
    
    75 70
             torConnectImageVisible = true,
    
    ... ... @@ -79,17 +74,15 @@ enum class ConnectAssistUiState(
    79 74
             titleDescriptionVisible = true,
    
    80 75
             titleDescriptionTextStringResource = R.string.preferences_tor_network_settings_explanation,
    
    81 76
             quickstartSwitchVisible = true,
    
    82
    -        unblockTheInternetInCountryDescriptionVisible = false,
    
    83 77
             countryDropDownVisible = false,
    
    84 78
             torBootstrapButton1Visible = false,
    
    85 79
             torBootstrapButton2Visible = true,
    
    86 80
             torBootstrapButton2TextStringResource = R.string.btn_cancel,
    
    87 81
             torBootstrapButton2ShouldOpenSettings = false,
    
    88 82
         ),
    
    89
    -    InternetError(
    
    83
    +    Offline(
    
    90 84
             progressBarVisible = true,
    
    91
    -        progress = 100,
    
    92
    -        progressTintColorResource = R.color.warning_yellow,
    
    85
    +        progressBackgroundTintColorResource = R.color.warning_yellow,
    
    93 86
             backButtonVisible = true,
    
    94 87
             settingsButtonVisible = true,
    
    95 88
             torConnectImageVisible = true,
    
    ... ... @@ -101,7 +94,6 @@ enum class ConnectAssistUiState(
    101 94
             internetErrorDescription = R.string.connection_assist_internet_error_description,
    
    102 95
             titleDescriptionTextStringResource = null,
    
    103 96
             quickstartSwitchVisible = false,
    
    104
    -        unblockTheInternetInCountryDescriptionVisible = false,
    
    105 97
             countryDropDownVisible = false,
    
    106 98
             torBootstrapButton1Visible = true,
    
    107 99
             torBootstrapButton1TextStringResource = R.string.connection_assist_internet_error_try_again,
    
    ... ... @@ -111,8 +103,6 @@ enum class ConnectAssistUiState(
    111 103
         ),
    
    112 104
         TryingAgain(
    
    113 105
             progressBarVisible = true,
    
    114
    -        progress = 0,
    
    115
    -        progressTintColorResource = null,
    
    116 106
             backButtonVisible = true,
    
    117 107
             settingsButtonVisible = true,
    
    118 108
             torConnectImageVisible = true,
    
    ... ... @@ -124,17 +114,15 @@ enum class ConnectAssistUiState(
    124 114
             internetErrorDescription = R.string.connection_assist_internet_error_description,
    
    125 115
             titleDescriptionTextStringResource = null,
    
    126 116
             quickstartSwitchVisible = false,
    
    127
    -        unblockTheInternetInCountryDescriptionVisible = false,
    
    128 117
             countryDropDownVisible = false,
    
    129 118
             torBootstrapButton1Visible = false,
    
    130 119
             torBootstrapButton2Visible = true,
    
    131 120
             torBootstrapButton2TextStringResource = R.string.btn_cancel,
    
    132 121
             torBootstrapButton2ShouldOpenSettings = false,
    
    133 122
         ),
    
    134
    -    ConnectionAssist(
    
    123
    +    ChooseRegion(
    
    135 124
             progressBarVisible = true,
    
    136
    -        progress = 100,
    
    137
    -        progressTintColorResource = R.color.warning_yellow,
    
    125
    +        progressBackgroundTintColorResource = R.color.warning_yellow,
    
    138 126
             backButtonVisible = true,
    
    139 127
             settingsButtonVisible = true,
    
    140 128
             torConnectImageVisible = true,
    
    ... ... @@ -146,19 +134,16 @@ enum class ConnectAssistUiState(
    146 134
             internetErrorDescription = R.string.connection_assist_try_a_bridge_description,
    
    147 135
             titleDescriptionTextStringResource = null,
    
    148 136
             quickstartSwitchVisible = false,
    
    149
    -        unblockTheInternetInCountryDescriptionVisible = true,
    
    150 137
             countryDropDownVisible = true,
    
    151 138
             torBootstrapButton1Visible = true,
    
    152 139
             torBootstrapButton1TextStringResource = R.string.connection_assist_try_a_bridge_button,
    
    153
    -        torBootstrapButton1ShouldShowTryingABridge = true,
    
    140
    +        torBootstrapButton1ShouldTryABridge = true,
    
    154 141
             torBootstrapButton2Visible = false,
    
    155 142
             torBootstrapButton2TextStringResource = null,
    
    156 143
             torBootstrapButton2ShouldOpenSettings = true,
    
    157 144
         ),
    
    158 145
         TryingABridge(
    
    159 146
             progressBarVisible = true,
    
    160
    -        progress = 0,
    
    161
    -        progressTintColorResource = null,
    
    162 147
             backButtonVisible = true,
    
    163 148
             settingsButtonVisible = true,
    
    164 149
             torConnectImageVisible = true,
    
    ... ... @@ -167,20 +152,18 @@ enum class ConnectAssistUiState(
    167 152
             titleLargeTextViewTextStringResource = R.string.connection_assist_trying_a_bridge_title,
    
    168 153
             titleDescriptionVisible = true,
    
    169 154
             learnMoreStringResource = R.string.connection_assist_internet_error_learn_more,
    
    170
    -        internetErrorDescription = R.string.connection_assist_try_a_bridge_description,
    
    155
    +        internetErrorDescription = ChooseRegion.internetErrorDescription,
    
    171 156
             titleDescriptionTextStringResource = null,
    
    172 157
             quickstartSwitchVisible = true,
    
    173
    -        unblockTheInternetInCountryDescriptionVisible = false,
    
    174 158
             countryDropDownVisible = false,
    
    175 159
             torBootstrapButton1Visible = false,
    
    176 160
             torBootstrapButton2Visible = true,
    
    177 161
             torBootstrapButton2TextStringResource = R.string.btn_cancel,
    
    178 162
             torBootstrapButton2ShouldOpenSettings = false,
    
    179 163
         ),
    
    180
    -    LocationError(
    
    164
    +    RegionNotFound(
    
    181 165
             progressBarVisible = true,
    
    182
    -        progress = 100,
    
    183
    -        progressTintColorResource = R.color.warning_yellow,
    
    166
    +        progressBackgroundTintColorResource = R.color.warning_yellow,
    
    184 167
             backButtonVisible = true,
    
    185 168
             settingsButtonVisible = true,
    
    186 169
             torConnectImageVisible = true,
    
    ... ... @@ -194,19 +177,39 @@ enum class ConnectAssistUiState(
    194 177
             internetErrorDescription2 = R.string.connection_assist_select_country_try_again,
    
    195 178
             titleDescriptionTextStringResource = null,
    
    196 179
             quickstartSwitchVisible = false,
    
    197
    -        unblockTheInternetInCountryDescriptionVisible = true,
    
    198 180
             countryDropDownVisible = true,
    
    181
    +        countryDropDownDefaultItem = R.string.connection_assist_select_country_or_region,
    
    199 182
             torBootstrapButton1Visible = true,
    
    200 183
             torBootstrapButton1TextStringResource = R.string.connection_assist_try_a_bridge_button,
    
    201
    -        torBootstrapButton1ShouldShowTryingABridge = true,
    
    184
    +        torBootstrapButton1ShouldTryABridge = true,
    
    202 185
             torBootstrapButton2Visible = false,
    
    203 186
             torBootstrapButton2TextStringResource = null,
    
    204 187
             torBootstrapButton2ShouldOpenSettings = true,
    
    205 188
         ),
    
    206
    -    LocationCheck(
    
    189
    +    TryingABridgeRegionNotFound(
    
    190
    +        progressBarVisible = true,
    
    191
    +        backButtonVisible = true,
    
    192
    +        settingsButtonVisible = true,
    
    193
    +        torConnectImageVisible = true,
    
    194
    +        torConnectImageResource = R.drawable.connect,
    
    195
    +        titleLargeTextViewVisible = true,
    
    196
    +        titleLargeTextViewTextStringResource = R.string.connection_assist_trying_a_bridge_title,
    
    197
    +        titleDescriptionVisible = true,
    
    198
    +        learnMoreStringResource = R.string.connection_assist_internet_error_learn_more,
    
    199
    +        internetErrorDescription = RegionNotFound.internetErrorDescription,
    
    200
    +        internetErrorDescription1 = RegionNotFound.internetErrorDescription1,
    
    201
    +        internetErrorDescription2 = RegionNotFound.internetErrorDescription2,
    
    202
    +        titleDescriptionTextStringResource = null,
    
    203
    +        quickstartSwitchVisible = true,
    
    204
    +        countryDropDownVisible = false,
    
    205
    +        torBootstrapButton1Visible = false,
    
    206
    +        torBootstrapButton2Visible = true,
    
    207
    +        torBootstrapButton2TextStringResource = R.string.btn_cancel,
    
    208
    +        torBootstrapButton2ShouldOpenSettings = false,
    
    209
    +    ),
    
    210
    +    ConfirmRegion(
    
    207 211
             progressBarVisible = true,
    
    208
    -        progress = 100,
    
    209
    -        progressTintColorResource = R.color.warning_yellow,
    
    212
    +        progressBackgroundTintColorResource = R.color.warning_yellow,
    
    210 213
             backButtonVisible = true,
    
    211 214
             settingsButtonVisible = true,
    
    212 215
             torConnectImageVisible = true,
    
    ... ... @@ -220,18 +223,38 @@ enum class ConnectAssistUiState(
    220 223
             internetErrorDescription2 = R.string.connection_assist_select_country_try_again,
    
    221 224
             titleDescriptionTextStringResource = null,
    
    222 225
             quickstartSwitchVisible = false,
    
    223
    -        unblockTheInternetInCountryDescriptionVisible = true,
    
    224 226
             countryDropDownVisible = true,
    
    227
    +        countryDropDownDefaultItem = R.string.connection_assist_select_country_or_region,
    
    225 228
             torBootstrapButton1Visible = true,
    
    226 229
             torBootstrapButton1TextStringResource = R.string.connection_assist_try_a_bridge_button,
    
    227
    -        torBootstrapButton1ShouldShowTryingABridge = true,
    
    230
    +        torBootstrapButton1ShouldTryABridge = true,
    
    228 231
             torBootstrapButton2Visible = false,
    
    229 232
             torBootstrapButton2TextStringResource = null,
    
    230 233
             torBootstrapButton2ShouldOpenSettings = true,
    
    231 234
         ),
    
    235
    +    TryingABridgeConfirmRegion(
    
    236
    +        progressBarVisible = true,
    
    237
    +        backButtonVisible = true,
    
    238
    +        settingsButtonVisible = true,
    
    239
    +        torConnectImageVisible = true,
    
    240
    +        torConnectImageResource = R.drawable.connect,
    
    241
    +        titleLargeTextViewVisible = true,
    
    242
    +        titleLargeTextViewTextStringResource = R.string.connection_assist_trying_a_bridge_title,
    
    243
    +        titleDescriptionVisible = true,
    
    244
    +        learnMoreStringResource = R.string.connection_assist_internet_error_learn_more,
    
    245
    +        internetErrorDescription = ConfirmRegion.internetErrorDescription,
    
    246
    +        internetErrorDescription1 = ConfirmRegion.internetErrorDescription1,
    
    247
    +        internetErrorDescription2 = ConfirmRegion.internetErrorDescription2,
    
    248
    +        titleDescriptionTextStringResource = null,
    
    249
    +        quickstartSwitchVisible = true,
    
    250
    +        countryDropDownVisible = false,
    
    251
    +        torBootstrapButton1Visible = false,
    
    252
    +        torBootstrapButton2Visible = true,
    
    253
    +        torBootstrapButton2TextStringResource = R.string.btn_cancel,
    
    254
    +        torBootstrapButton2ShouldOpenSettings = false,
    
    255
    +    ),
    
    232 256
         LastTry(
    
    233 257
             progressBarVisible = true,
    
    234
    -        progress = 0,
    
    235 258
             backButtonVisible = true,
    
    236 259
             settingsButtonVisible = true,
    
    237 260
             torConnectImageVisible = true,
    
    ... ... @@ -245,7 +268,6 @@ enum class ConnectAssistUiState(
    245 268
             internetErrorDescription2 = R.string.connection_assist_select_country_try_again,
    
    246 269
             titleDescriptionTextStringResource = null,
    
    247 270
             quickstartSwitchVisible = true,
    
    248
    -        unblockTheInternetInCountryDescriptionVisible = false,
    
    249 271
             countryDropDownVisible = false,
    
    250 272
             torBootstrapButton1Visible = false,
    
    251 273
             torBootstrapButton2Visible = true,
    
    ... ... @@ -254,8 +276,7 @@ enum class ConnectAssistUiState(
    254 276
         ),
    
    255 277
         FinalError(
    
    256 278
             progressBarVisible = true,
    
    257
    -        progress = 100,
    
    258
    -        progressTintColorResource = R.color.warning_yellow,
    
    279
    +        progressBackgroundTintColorResource = R.color.warning_yellow,
    
    259 280
             backButtonVisible = true,
    
    260 281
             settingsButtonVisible = true,
    
    261 282
             torConnectImageVisible = true,
    
    ... ... @@ -268,10 +289,10 @@ enum class ConnectAssistUiState(
    268 289
             internetErrorDescription1 = R.string.connection_assist_final_error_troubleshoot_connection_link,
    
    269 290
             titleDescriptionTextStringResource = null,
    
    270 291
             quickstartSwitchVisible = false,
    
    271
    -        unblockTheInternetInCountryDescriptionVisible = false,
    
    272 292
             countryDropDownVisible = false,
    
    273 293
             torBootstrapButton1Visible = true,
    
    274 294
             torBootstrapButton1TextStringResource = R.string.connection_assist_configure_connection_button,
    
    295
    +        torBootstrapButton1ShouldOpenSettings = true,
    
    275 296
             torBootstrapButton2Visible = true,
    
    276 297
             torBootstrapButton2TextStringResource = R.string.mozac_lib_crash_dialog_button_restart,
    
    277 298
             torBootstrapButton2ShouldOpenSettings = false,
    

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/QuickstartViewModel.kt
    ... ... @@ -12,19 +12,19 @@ class QuickstartViewModel(
    12 12
     ) : AndroidViewModel(application) {
    
    13 13
     
    
    14 14
         private val components = getApplication<Application>().components
    
    15
    -    private val torIntegrationAndroid =
    
    15
    +    private val torAndroidIntegration =
    
    16 16
             (components.core.engine as GeckoEngine).getTorIntegrationController()
    
    17 17
     
    
    18 18
         /**
    
    19 19
          * NOTE: Whilst the initial value for _quickstart is fetched from
    
    20
    -     * TorIntegrationAndroid.quickstartGet (which is surfaced from TorConnect.quickstart), and we
    
    20
    +     * TorAndroidIntegration.quickstartGet (which is surfaced from TorConnect.quickstart), and we
    
    21 21
          * pass on any changes in value up to TorConnect.quickstart (via quickstartSet()), we do not
    
    22 22
          * listen for any changes to the TorConnect.quickstart value via "QuickstartChange" because we
    
    23 23
          * do not expect anything outside of TorConnectViewModel to change its value, so we expect its
    
    24 24
          * value to remain in sync with our local value.
    
    25 25
          */
    
    26 26
         init {
    
    27
    -        torIntegrationAndroid.quickstartGet {
    
    27
    +        torAndroidIntegration.quickstartGet {
    
    28 28
                 _quickstart.value = it
    
    29 29
                 components.settings.quickStart = it
    
    30 30
             }
    
    ... ... @@ -36,7 +36,7 @@ class QuickstartViewModel(
    36 36
         }
    
    37 37
     
    
    38 38
         fun quickstartSet(value: Boolean) {
    
    39
    -        torIntegrationAndroid.quickstartSet(value)
    
    39
    +        torAndroidIntegration.quickstartSet(value)
    
    40 40
             _quickstart.value = value
    
    41 41
             components.settings.quickStart = value
    
    42 42
         }
    

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorBootstrapProgressViewModel.kt
    ... ... @@ -11,7 +11,7 @@ class TorBootstrapProgressViewModel(
    11 11
         application: Application,
    
    12 12
     ) : AndroidViewModel(application), BootstrapStateChangeListener {
    
    13 13
     
    
    14
    -    private val torIntegrationAndroid =
    
    14
    +    private val torAndroidIntegration =
    
    15 15
             application.components.core.geckoRuntime.torIntegrationController
    
    16 16
     
    
    17 17
         val progress: MutableLiveData<Int> by lazy {
    
    ... ... @@ -19,11 +19,11 @@ class TorBootstrapProgressViewModel(
    19 19
         }
    
    20 20
     
    
    21 21
         init {
    
    22
    -        torIntegrationAndroid.registerBootstrapStateChangeListener(this)
    
    22
    +        torAndroidIntegration.registerBootstrapStateChangeListener(this)
    
    23 23
         }
    
    24 24
     
    
    25 25
         override fun onCleared() {
    
    26
    -        torIntegrationAndroid.unregisterBootstrapStateChangeListener(this)
    
    26
    +        torAndroidIntegration.unregisterBootstrapStateChangeListener(this)
    
    27 27
             super.onCleared()
    
    28 28
         }
    
    29 29
     
    

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt
    ... ... @@ -4,6 +4,8 @@
    4 4
     
    
    5 5
     package org.mozilla.fenix.tor
    
    6 6
     
    
    7
    +import android.content.BroadcastReceiver
    
    8
    +import android.content.Context
    
    7 9
     import android.content.Intent
    
    8 10
     import android.graphics.Color
    
    9 11
     import android.os.Build
    
    ... ... @@ -17,7 +19,10 @@ import android.util.Log
    17 19
     import android.view.LayoutInflater
    
    18 20
     import android.view.View
    
    19 21
     import android.view.ViewGroup
    
    22
    +import android.widget.AdapterView
    
    23
    +import android.widget.ArrayAdapter
    
    20 24
     import androidx.appcompat.content.res.AppCompatResources
    
    25
    +import androidx.core.view.isEmpty
    
    21 26
     import androidx.fragment.app.Fragment
    
    22 27
     import androidx.fragment.app.activityViewModels
    
    23 28
     import androidx.fragment.app.viewModels
    
    ... ... @@ -38,7 +43,6 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
    38 43
         private val progressViewModel: TorBootstrapProgressViewModel by viewModels()
    
    39 44
         private val quickstartViewModel: QuickstartViewModel by activityViewModels()
    
    40 45
         private val torConnectionAssistViewModel : TorConnectionAssistViewModel by viewModels()
    
    41
    -    private val urlQuickLoadViewModel : UrlQuickLoadViewModel by activityViewModels()
    
    42 46
     
    
    43 47
         private var _binding: FragmentTorConnectionAssistBinding? = null
    
    44 48
         private val binding get() = _binding!!
    
    ... ... @@ -52,9 +56,18 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
    52 56
                 inflater, container, false,
    
    53 57
             )
    
    54 58
     
    
    59
    +        object : BroadcastReceiver() {
    
    60
    +            override fun onReceive(context: Context, intent: Intent) {
    
    61
    +                if (intent.action === Intent.ACTION_LOCALE_CHANGED) {
    
    62
    +                    Log.v("LocaleReceiver", "received ACTION_LOCALE_CHANGED")
    
    63
    +                    torConnectionAssistViewModel.fetchCountryNamesGet()
    
    64
    +                }
    
    65
    +            }
    
    66
    +        }
    
    67
    +
    
    55 68
             viewLifecycleOwner.lifecycleScope.launch {
    
    56 69
                 repeatOnLifecycle(Lifecycle.State.STARTED) {
    
    57
    -                torConnectionAssistViewModel.collectLastKnownStatus()
    
    70
    +                torConnectionAssistViewModel.collectTorConnectStage()
    
    58 71
                 }
    
    59 72
             }
    
    60 73
     
    
    ... ... @@ -65,12 +78,6 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
    65 78
                 }
    
    66 79
             }
    
    67 80
     
    
    68
    -        urlQuickLoadViewModel.urlToLoadAfterConnecting.observe(viewLifecycleOwner) { url ->
    
    69
    -            if (!url.isNullOrBlank()) {
    
    70
    -                torConnectionAssistViewModel.handleConnect()
    
    71
    -            }
    
    72
    -        }
    
    73
    -
    
    74 81
             return binding.root
    
    75 82
         }
    
    76 83
     
    
    ... ... @@ -129,14 +136,10 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
    129 136
         private fun setProgressBar(screen: ConnectAssistUiState) {
    
    130 137
             binding.torBootstrapProgressBar.visibility =
    
    131 138
                 if (screen.progressBarVisible) View.VISIBLE else View.GONE
    
    132
    -        binding.torBootstrapProgressBar.progress = screen.progress
    
    133
    -        binding.torBootstrapProgressBar.progressTintList =
    
    134
    -            screen.progressTintColorResource?.let {
    
    135
    -                AppCompatResources.getColorStateList(
    
    136
    -                    requireContext(),
    
    137
    -                    it,
    
    138
    -                )
    
    139
    -            }
    
    139
    +        binding.torBootstrapProgressBar.progressBackgroundTintList = AppCompatResources.getColorStateList(
    
    140
    +            requireContext(),
    
    141
    +            screen.progressBackgroundTintColorResource,
    
    142
    +        )
    
    140 143
         }
    
    141 144
     
    
    142 145
         private fun setSettingsButton(screen: ConnectAssistUiState) {
    
    ... ... @@ -201,17 +204,126 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
    201 204
         }
    
    202 205
     
    
    203 206
         private fun setCountryDropDown(screen: ConnectAssistUiState) {
    
    204
    -        binding.unblockTheInternetInCountryDescription.visibility =
    
    205
    -            if (screen.unblockTheInternetInCountryDescriptionVisible) View.VISIBLE else View.GONE
    
    206
    -        binding.countryDropDown.visibility = if (screen.countryDropDownVisible) View.VISIBLE else View.GONE
    
    207
    +        if (screen.countryDropDownVisible) {
    
    208
    +            val spinnerAdapter: ArrayAdapter<String> = initializeSpinner()
    
    209
    +            if (binding.countryDropDown.isEmpty()) {
    
    210
    +                populateCountryDropDown(spinnerAdapter)
    
    211
    +                setOnItemSelectedListener()
    
    212
    +            }
    
    213
    +
    
    214
    +            setFirstItemInCountryDropDown(spinnerAdapter, getString(screen.countryDropDownDefaultItem))
    
    215
    +
    
    216
    +            if (screen == ConnectAssistUiState.ChooseRegion || screen == ConnectAssistUiState.ConfirmRegion || screen == ConnectAssistUiState.RegionNotFound) {
    
    217
    +                torConnectionAssistViewModel.selectDefaultRegion()
    
    218
    +                binding.countryDropDown.setSelection(spinnerAdapter.getPosition(torConnectionAssistViewModel.selectedCountryCode.value))
    
    219
    +            }
    
    220
    +
    
    221
    +            binding.unblockTheInternetInCountryDescription.visibility = View.VISIBLE
    
    222
    +            binding.countryDropDown.visibility = View.VISIBLE
    
    223
    +        } else {
    
    224
    +            binding.unblockTheInternetInCountryDescription.visibility = View.GONE
    
    225
    +            binding.countryDropDown.visibility = View.GONE
    
    226
    +        }
    
    227
    +    }
    
    228
    +
    
    229
    +    private fun setFirstItemInCountryDropDown(
    
    230
    +        spinnerAdapter: ArrayAdapter<String>,
    
    231
    +        item: String,
    
    232
    +    ) {
    
    233
    +        if (!spinnerAdapter.isEmpty) {
    
    234
    +            spinnerAdapter.remove(spinnerAdapter.getItem(0))
    
    235
    +        }
    
    236
    +        spinnerAdapter.insert(item, 0)
    
    237
    +    }
    
    238
    +
    
    239
    +    private fun initializeSpinner(): ArrayAdapter<String> {
    
    240
    +        val spinnerAdapter: ArrayAdapter<String> =
    
    241
    +            ArrayAdapter<String>(
    
    242
    +                requireContext(),
    
    243
    +                android.R.layout.simple_spinner_item,
    
    244
    +                android.R.id.text1,
    
    245
    +            )
    
    246
    +        spinnerAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
    
    247
    +        binding.countryDropDown.adapter = spinnerAdapter
    
    248
    +        return spinnerAdapter
    
    249
    +    }
    
    250
    +
    
    251
    +    private fun populateCountryDropDown(spinnerAdapter: ArrayAdapter<String>) {
    
    252
    +        torConnectionAssistViewModel.fetchCountryNamesGet()
    
    253
    +        viewLifecycleOwner.lifecycleScope.launch {
    
    254
    +            repeatOnLifecycle(Lifecycle.State.STARTED) {
    
    255
    +                torConnectionAssistViewModel.countryCodeNameMap.collect {
    
    256
    +                    Log.d(TAG, "countryCodeNameMap: $it")
    
    257
    +                    if (it != null) {
    
    258
    +                        spinnerAdapter.clear()
    
    259
    +                        spinnerAdapter.add(getString(torConnectionAssistViewModel.torConnectScreen.value.countryDropDownDefaultItem))
    
    260
    +                        spinnerAdapter.addAll(it.values)
    
    261
    +                    }
    
    262
    +                }
    
    263
    +            }
    
    264
    +        }
    
    265
    +    }
    
    266
    +
    
    267
    +    private fun setOnItemSelectedListener() {
    
    268
    +        binding.countryDropDown.onItemSelectedListener =
    
    269
    +            object : AdapterView.OnItemSelectedListener {
    
    270
    +                override fun onItemSelected(
    
    271
    +                    parent: AdapterView<*>?,
    
    272
    +                    view: View?,
    
    273
    +                    position: Int,
    
    274
    +                    id: Long,
    
    275
    +                ) {
    
    276
    +                    torConnectionAssistViewModel.setCountryCodeToSelectedItem(position)
    
    277
    +                    updateButton1(torConnectionAssistViewModel.torConnectScreen.value)
    
    278
    +                }
    
    279
    +
    
    280
    +                override fun onNothingSelected(parent: AdapterView<*>?) {}
    
    281
    +            }
    
    207 282
         }
    
    208 283
     
    
    209 284
         private fun setButton1(screen: ConnectAssistUiState) {
    
    210
    -        binding.torBootstrapButton1.visibility =
    
    211
    -            if (screen.torBootstrapButton1Visible) View.VISIBLE else View.GONE
    
    212
    -        binding.torBootstrapButton1.text = getString(screen.torBootstrapButton1TextStringResource)
    
    213
    -        binding.torBootstrapButton1.setOnClickListener {
    
    214
    -            torConnectionAssistViewModel.handleConnect()
    
    285
    +        binding.torBootstrapButton1.apply {
    
    286
    +            visibility =
    
    287
    +                if (screen.torBootstrapButton1Visible) View.VISIBLE else View.GONE
    
    288
    +            text = getString(screen.torBootstrapButton1TextStringResource)
    
    289
    +            setOnClickListener {
    
    290
    +                if (screen.torBootstrapButton1ShouldOpenSettings) {
    
    291
    +                    openTorConnectionSettings()
    
    292
    +                } else {
    
    293
    +                    torConnectionAssistViewModel.handleConnect()
    
    294
    +                }
    
    295
    +            }
    
    296
    +            updateButton1(screen)
    
    297
    +        }
    
    298
    +    }
    
    299
    +
    
    300
    +    private fun updateButton1(screen: ConnectAssistUiState) {
    
    301
    +        binding.torBootstrapButton1.apply {
    
    302
    +            if (!torConnectionAssistViewModel.button1ShouldBeDisabled(screen)) {
    
    303
    +                isEnabled = true
    
    304
    +                backgroundTintList = AppCompatResources.getColorStateList(
    
    305
    +                    requireContext(),
    
    306
    +                    R.color.connect_button_purple,
    
    307
    +                )
    
    308
    +                setTextColor(
    
    309
    +                    AppCompatResources.getColorStateList(
    
    310
    +                        requireContext(),
    
    311
    +                        R.color.photonLightGrey05,
    
    312
    +                    ),
    
    313
    +                )
    
    314
    +            } else {
    
    315
    +                isEnabled = false
    
    316
    +                backgroundTintList = AppCompatResources.getColorStateList(
    
    317
    +                    requireContext(),
    
    318
    +                    R.color.disabled_connect_button_purple,
    
    319
    +                )
    
    320
    +                setTextColor(
    
    321
    +                    AppCompatResources.getColorStateList(
    
    322
    +                        requireContext(),
    
    323
    +                        R.color.disabled_text_gray_purple,
    
    324
    +                    ),
    
    325
    +                )
    
    326
    +            }
    
    215 327
             }
    
    216 328
         }
    
    217 329
     
    
    ... ... @@ -235,13 +347,12 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
    235 347
                     }
    
    236 348
             }
    
    237 349
             binding.torBootstrapButton2.setOnClickListener {
    
    238
    -            torConnectionAssistViewModel.cancelTorBootstrap()
    
    239 350
                 if (screen.torBootstrapButton2ShouldOpenSettings) {
    
    240 351
                     openTorConnectionSettings()
    
    241 352
                 } else if (screen.torBootstrapButton2ShouldRestartApp) {
    
    242 353
                     restartApplication()
    
    243 354
                 } else {
    
    244
    -                showScreen(ConnectAssistUiState.Configuring)
    
    355
    +                torConnectionAssistViewModel.cancelTorBootstrap()
    
    245 356
                 }
    
    246 357
             }
    
    247 358
         }
    
    ... ... @@ -297,11 +408,7 @@ class TorConnectionAssistFragment : Fragment(), UserInteractionHandler {
    297 408
         }
    
    298 409
     
    
    299 410
         private fun openTorConnectionSettings() {
    
    300
    -        findNavController().navigate(
    
    301
    -            TorConnectionAssistFragmentDirections.actionTorConnectionAssistFragmentToSettingsFragment(
    
    302
    -                requireContext().getString(R.string.pref_key_connection)
    
    303
    -            ),
    
    304
    -        )
    
    411
    +        openSettings(requireContext().getString(R.string.pref_key_connection))
    
    305 412
         }
    
    306 413
     
    
    307 414
         private fun restartApplication() {
    

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt
    ... ... @@ -11,186 +11,148 @@ import androidx.lifecycle.MutableLiveData
    11 11
     import kotlinx.coroutines.flow.MutableStateFlow
    
    12 12
     import kotlinx.coroutines.flow.StateFlow
    
    13 13
     import org.mozilla.fenix.HomeActivity
    
    14
    +import org.mozilla.fenix.R
    
    14 15
     import org.mozilla.fenix.ext.components
    
    16
    +import org.mozilla.gecko.util.GeckoBundle
    
    17
    +import org.mozilla.geckoview.TorAndroidIntegration.BootstrapStateChangeListener
    
    18
    +import org.mozilla.geckoview.TorConnectStage
    
    19
    +import org.mozilla.geckoview.TorConnectStageName
    
    15 20
     
    
    16 21
     class TorConnectionAssistViewModel(
    
    17 22
         application: Application,
    
    18
    -) : AndroidViewModel(application) {
    
    23
    +) : AndroidViewModel(application), BootstrapStateChangeListener {
    
    19 24
     
    
    20 25
         private val TAG = "torConnectionAssistVM"
    
    21
    -    private val torIntegrationAndroid =
    
    26
    +    private val torAndroidIntegration =
    
    22 27
             application.components.core.geckoRuntime.torIntegrationController
    
    23
    -    private val _torController: TorControllerGV = application.components.torController
    
    24 28
     
    
    25
    -    private val _torConnectScreen = MutableStateFlow(ConnectAssistUiState.Splash)
    
    26
    -    internal val torConnectScreen: StateFlow<ConnectAssistUiState> = _torConnectScreen
    
    27
    -
    
    28
    -    val shouldOpenHome: MutableLiveData<Boolean> by lazy {
    
    29
    -        MutableLiveData(false)
    
    29
    +    init {
    
    30
    +        torAndroidIntegration.registerBootstrapStateChangeListener(this)
    
    30 31
         }
    
    31 32
     
    
    32
    -    fun handleConnect() {
    
    33
    -        if (_torConnectScreen.value.torBootstrapButton1ShouldShowTryingABridge) {
    
    34
    -            tryABridge()
    
    35
    -        } else {
    
    36
    -            if (_torController.lastKnownStatus.value.isOff()) {
    
    37
    -                torIntegrationAndroid.beginBootstrap()
    
    33
    +    fun fetchCountryNamesGet() {
    
    34
    +        torAndroidIntegration.countryNamesGet { countryNames : GeckoBundle? ->
    
    35
    +            if (countryNames != null) {
    
    36
    +                val codes: Array<String> = countryNames.keys()
    
    37
    +                val regions = mutableMapOf<String, String>()
    
    38
    +                for (code in codes) {
    
    39
    +                    regions[code] = countryNames.getString(code)
    
    40
    +                }
    
    41
    +                countryCodeNameMap.value = regions
    
    38 42
                 }
    
    39 43
             }
    
    40 44
         }
    
    41 45
     
    
    42
    -    fun cancelTorBootstrap() {
    
    43
    -        torIntegrationAndroid.cancelBootstrap()
    
    44
    -        _torController.setTorStopped()
    
    45
    -    }
    
    46
    -
    
    47
    -    suspend fun collectLastKnownStatus() {
    
    48
    -        _torController.lastKnownStatus.collect {
    
    49
    -            when (it) {
    
    50
    -                TorConnectState.Initial -> _torConnectScreen.value = ConnectAssistUiState.Splash
    
    51
    -                TorConnectState.Configuring -> handleConfiguring()
    
    52
    -                TorConnectState.AutoBootstrapping -> handleBootstrap()
    
    53
    -                TorConnectState.Bootstrapping -> handleBootstrap()
    
    54
    -                TorConnectState.Bootstrapped -> shouldOpenHome.value = true
    
    55
    -                TorConnectState.Disabled -> shouldOpenHome.value = true
    
    56
    -                TorConnectState.Error -> handleError()
    
    57
    -            }
    
    58
    -        }
    
    46
    +    override fun onCleared() {
    
    47
    +        torAndroidIntegration.unregisterBootstrapStateChangeListener(this)
    
    48
    +        super.onCleared()
    
    59 49
         }
    
    60 50
     
    
    61
    -    private fun handleConfiguring() {
    
    62
    -        if (_torController.lastKnownError == null) {
    
    63
    -            _torConnectScreen.value = ConnectAssistUiState.Configuring
    
    64
    -        } else {
    
    65
    -            handleError()
    
    66
    -        }
    
    51
    +    private val torConnectStage: MutableStateFlow<TorConnectStage?> by lazy {
    
    52
    +        MutableStateFlow(torAndroidIntegration.lastKnowStage.value)
    
    67 53
         }
    
    68 54
     
    
    69
    -    private fun handleBootstrap() {
    
    70
    -        when (_torConnectScreen.value) {
    
    71
    -            ConnectAssistUiState.InternetError -> {
    
    72
    -                _torConnectScreen.value = ConnectAssistUiState.TryingAgain
    
    73
    -            }
    
    74
    -
    
    75
    -            ConnectAssistUiState.TryingAgain -> {
    
    76
    -                /** stay here */
    
    77
    -            }
    
    78
    -
    
    79
    -            ConnectAssistUiState.ConnectionAssist -> {
    
    80
    -                _torConnectScreen.value = ConnectAssistUiState.TryingABridge
    
    81
    -            }
    
    82
    -
    
    83
    -            ConnectAssistUiState.LocationError -> {
    
    84
    -                _torConnectScreen.value = ConnectAssistUiState.TryingABridge
    
    85
    -            }
    
    55
    +    private val _torConnectScreen = MutableStateFlow(ConnectAssistUiState.Loading)
    
    56
    +    internal val torConnectScreen: StateFlow<ConnectAssistUiState> = _torConnectScreen
    
    86 57
     
    
    87
    -            ConnectAssistUiState.TryingABridge -> {
    
    88
    -                /** stay here */
    
    89
    -            }
    
    58
    +    val countryCodeNameMap: MutableStateFlow<Map<String, String>?> by lazy {
    
    59
    +        MutableStateFlow(null)
    
    60
    +    }
    
    90 61
     
    
    91
    -            ConnectAssistUiState.LocationCheck -> {
    
    92
    -                _torConnectScreen.value = ConnectAssistUiState.LastTry
    
    93
    -            }
    
    62
    +    val selectedCountryCode: MutableStateFlow<String> by lazy {
    
    63
    +        MutableStateFlow("automatic")
    
    64
    +    }
    
    94 65
     
    
    95
    -            ConnectAssistUiState.LastTry -> {
    
    96
    -                /** stay here */
    
    97
    -            }
    
    66
    +    fun selectDefaultRegion() {
    
    67
    +        selectedCountryCode.value = torConnectStage.value?.defaultRegion ?: "automatic"
    
    68
    +    }
    
    98 69
     
    
    99
    -            else -> _torConnectScreen.value =
    
    100
    -                ConnectAssistUiState.Connecting
    
    101
    -        }
    
    70
    +    fun setCountryCodeToSelectedItem(position: Int) {
    
    71
    +        selectedCountryCode.value =
    
    72
    +            countryCodeNameMap.value?.keys?.toList()
    
    73
    +                ?.getOrNull(position - 1) ?: "automatic"
    
    74
    +        // position - 1 since we have the default/first value of automatic
    
    75
    +        Log.d(TAG, "selectedCountryCode = ${selectedCountryCode.value}")
    
    102 76
         }
    
    103 77
     
    
    104
    -    private fun handleError() {
    
    105
    -        _torController.lastKnownError?.apply {
    
    106
    -            Log.d(
    
    107
    -                TAG,
    
    108
    -                "TorError(message = $message, details = $details, phase = $phase, reason = $reason",
    
    109
    -            )
    
    110
    -            // TODO better error handling
    
    111
    -            when (reason) {
    
    112
    -//                "noroute" -> handleNoRoute() TODO re-add when working better
    
    113
    -                else -> handleUnknownError()
    
    114
    -            }
    
    115
    -        }
    
    78
    +    val shouldOpenHome: MutableLiveData<Boolean> by lazy {
    
    79
    +        MutableLiveData(false)
    
    116 80
         }
    
    117 81
     
    
    118
    -    private fun handleNoRoute() {
    
    119
    -        Log.d(TAG, "handleNoRoute(), _torConnectScreen.value = ${_torConnectScreen.value}")
    
    120
    -        when (_torConnectScreen.value) {
    
    121
    -            ConnectAssistUiState.Connecting -> _torConnectScreen.value = ConnectAssistUiState.ConnectionAssist
    
    122
    -            ConnectAssistUiState.ConnectionAssist -> {/** no op, likely a duplicate error */}
    
    123
    -            ConnectAssistUiState.TryingABridge -> _torConnectScreen.value = ConnectAssistUiState.LocationCheck
    
    124
    -            ConnectAssistUiState.LocationCheck -> {/** no op, likely a duplicate error */}
    
    125
    -            ConnectAssistUiState.LastTry -> _torConnectScreen.value = ConnectAssistUiState.FinalError
    
    126
    -            ConnectAssistUiState.FinalError -> {/** no op, likely a duplicate error */}
    
    127
    -            else -> _torConnectScreen.value = ConnectAssistUiState.InternetError
    
    82
    +    fun handleConnect() {
    
    83
    +        val screen = _torConnectScreen.value
    
    84
    +        if (screen.torBootstrapButton1ShouldTryABridge && !button1ShouldBeDisabled(screen)) {
    
    85
    +            Log.d(TAG, "beginAutoBootstrap with countryCode: ${selectedCountryCode.value}")
    
    86
    +            torAndroidIntegration.beginAutoBootstrap(selectedCountryCode.value)
    
    87
    +        } else {
    
    88
    +            torAndroidIntegration.beginBootstrap()
    
    128 89
             }
    
    129 90
         }
    
    130 91
     
    
    131
    -    private fun handleUnknownError() {
    
    132
    -        // TODO should we have a dedicated screen for unknown errors?
    
    133
    -        _torConnectScreen.value = ConnectAssistUiState.InternetError
    
    92
    +    fun cancelTorBootstrap() {
    
    93
    +        torAndroidIntegration.cancelBootstrap()
    
    134 94
         }
    
    135 95
     
    
    136
    -    private fun tryABridge() {
    
    137
    -        if (!locationFound()) {
    
    138
    -            _torConnectScreen.value = ConnectAssistUiState.LocationError
    
    139
    -            return
    
    140
    -        }
    
    141
    -        if (!_torController.bridgesEnabled) {
    
    142
    -            _torController.bridgesEnabled = true
    
    143
    -            _torController.bridgeTransport =
    
    144
    -                TorBridgeTransportConfig.BUILTIN_SNOWFLAKE // TODO select based on country
    
    96
    +    suspend fun collectTorConnectStage() {
    
    97
    +        torConnectStage.collect {
    
    98
    +            Log.d(TAG, "torConnectStageName: ${it?.name}")
    
    99
    +            when (it?.name) {
    
    100
    +                TorConnectStageName.Disabled       -> shouldOpenHome.value = true // TODO use TorConnect.enabled instead to determine this
    
    101
    +                TorConnectStageName.Loading        -> _torConnectScreen.value = ConnectAssistUiState.Loading
    
    102
    +                TorConnectStageName.Start          -> _torConnectScreen.value = ConnectAssistUiState.Start
    
    103
    +                TorConnectStageName.Bootstrapping  -> _torConnectScreen.value = handleBootstrapTrigger(it.bootstrapTrigger)
    
    104
    +                TorConnectStageName.Offline        -> _torConnectScreen.value = ConnectAssistUiState.Offline
    
    105
    +                TorConnectStageName.ChooseRegion   -> _torConnectScreen.value = ConnectAssistUiState.ChooseRegion
    
    106
    +                TorConnectStageName.RegionNotFound -> _torConnectScreen.value = ConnectAssistUiState.RegionNotFound
    
    107
    +                TorConnectStageName.ConfirmRegion  -> _torConnectScreen.value = ConnectAssistUiState.ConfirmRegion
    
    108
    +                TorConnectStageName.FinalError     -> _torConnectScreen.value = ConnectAssistUiState.FinalError
    
    109
    +                TorConnectStageName.Bootstrapped   -> shouldOpenHome.value = true
    
    110
    +                null                               -> {}
    
    111
    +            }
    
    145 112
             }
    
    146
    -        torIntegrationAndroid.beginBootstrap()
    
    147 113
         }
    
    148 114
     
    
    149
    -    private fun locationFound(): Boolean {
    
    150
    -        // TODO try to find location
    
    151
    -        return true
    
    115
    +    private fun handleBootstrapTrigger(bootstrapTrigger: TorConnectStageName) : ConnectAssistUiState {
    
    116
    +        Log.d(TAG, "bootstrapTrigger: $bootstrapTrigger")
    
    117
    +        return when (bootstrapTrigger) {
    
    118
    +            TorConnectStageName.Start          -> ConnectAssistUiState.Bootstrapping
    
    119
    +            TorConnectStageName.Offline        -> ConnectAssistUiState.TryingAgain
    
    120
    +            TorConnectStageName.ChooseRegion   -> ConnectAssistUiState.TryingABridge
    
    121
    +            TorConnectStageName.RegionNotFound -> ConnectAssistUiState.TryingABridgeRegionNotFound
    
    122
    +            TorConnectStageName.ConfirmRegion  -> ConnectAssistUiState.TryingABridgeConfirmRegion
    
    123
    +            else                               -> {
    
    124
    +                Log.e(TAG, "Unexpected bootstrapTrigger of $bootstrapTrigger")
    
    125
    +                ConnectAssistUiState.TryingAgain
    
    126
    +            }
    
    127
    +        }
    
    152 128
         }
    
    153 129
     
    
    154 130
         fun handleBackButtonPressed(homeActivity: HomeActivity) {
    
    155 131
             when (torConnectScreen.value) {
    
    156
    -            ConnectAssistUiState.Splash -> homeActivity.shutDown()
    
    157
    -            ConnectAssistUiState.Configuring -> homeActivity.shutDown()
    
    158
    -            ConnectAssistUiState.Connecting -> cancelTorBootstrap()
    
    159
    -            ConnectAssistUiState.InternetError -> {
    
    160
    -                _torController.lastKnownError = null
    
    161
    -                _torConnectScreen.value = ConnectAssistUiState.Configuring
    
    162
    -            }
    
    163
    -
    
    164
    -            ConnectAssistUiState.TryingAgain -> {
    
    165
    -                cancelTorBootstrap()
    
    166
    -            }
    
    132
    +            ConnectAssistUiState.Loading -> homeActivity.shutDown()
    
    133
    +            ConnectAssistUiState.Start   -> homeActivity.shutDown()
    
    134
    +            else                         -> torAndroidIntegration.startAgain()
    
    135
    +        }
    
    136
    +    }
    
    167 137
     
    
    168
    -            ConnectAssistUiState.ConnectionAssist -> {
    
    169
    -                _torController.lastKnownError = null
    
    170
    -                _torConnectScreen.value = ConnectAssistUiState.Configuring
    
    171
    -            }
    
    138
    +    override fun onBootstrapStateChange(state: String?) {}
    
    172 139
     
    
    173
    -            ConnectAssistUiState.TryingABridge -> {
    
    174
    -                _torController.stopTor()
    
    175
    -                _torConnectScreen.value = ConnectAssistUiState.ConnectionAssist
    
    176
    -            }
    
    140
    +    override fun onBootstrapStageChange(stage: TorConnectStage?) {
    
    141
    +        torConnectStage.value = stage
    
    142
    +    }
    
    177 143
     
    
    178
    -            ConnectAssistUiState.LocationError -> {
    
    179
    -                _torConnectScreen.value = ConnectAssistUiState.ConnectionAssist
    
    180
    -            }
    
    144
    +    override fun onBootstrapProgress(progress: Double, hasWarnings: Boolean) {}
    
    181 145
     
    
    182
    -            ConnectAssistUiState.LocationCheck -> {
    
    183
    -                _torConnectScreen.value = ConnectAssistUiState.LocationError
    
    184
    -            }
    
    146
    +    override fun onBootstrapComplete() {}
    
    185 147
     
    
    186
    -            ConnectAssistUiState.LastTry -> {
    
    187
    -                _torController.stopTor()
    
    188
    -                _torConnectScreen.value = ConnectAssistUiState.LocationCheck
    
    189
    -            }
    
    148
    +    override fun onBootstrapError(
    
    149
    +        code: String?,
    
    150
    +        message: String?,
    
    151
    +        phase: String?,
    
    152
    +        reason: String?,
    
    153
    +    ) {}
    
    190 154
     
    
    191
    -            ConnectAssistUiState.FinalError -> {
    
    192
    -                _torConnectScreen.value = ConnectAssistUiState.LocationCheck
    
    193
    -            }
    
    194
    -        }
    
    155
    +    fun button1ShouldBeDisabled(screen: ConnectAssistUiState): Boolean {
    
    156
    +        return selectedCountryCode.value == "automatic" && screen.countryDropDownDefaultItem == R.string.connection_assist_select_country_or_region
    
    195 157
         }
    
    196 158
     }

  • mobile/android/fenix/app/src/main/java/org/mozilla/fenix/tor/UrlQuickLoadViewModel.kt
    1 1
     package org.mozilla.fenix.tor
    
    2 2
     
    
    3
    +import android.app.Application
    
    4
    +import androidx.lifecycle.AndroidViewModel
    
    3 5
     import androidx.lifecycle.MutableLiveData
    
    4
    -import androidx.lifecycle.ViewModel
    
    6
    +import org.mozilla.fenix.ext.components
    
    7
    +import org.mozilla.geckoview.TorConnectStageName
    
    8
    +
    
    9
    +class UrlQuickLoadViewModel(application: Application) : AndroidViewModel(application) {
    
    10
    +
    
    11
    +    private val torAndroidIntegration =
    
    12
    +        application.components.core.geckoRuntime.torIntegrationController
    
    5 13
     
    
    6
    -class UrlQuickLoadViewModel : ViewModel() {
    
    7 14
         val urlToLoadAfterConnecting: MutableLiveData<String?> by lazy {
    
    8 15
             MutableLiveData<String?>(null)
    
    9 16
         }
    
    17
    +
    
    18
    +    fun maybeBeginBootstrap() {
    
    19
    +        when (torAndroidIntegration.lastKnowStage.value?.name) {
    
    20
    +            TorConnectStageName.Offline -> torAndroidIntegration.beginBootstrap()
    
    21
    +            TorConnectStageName.Start -> torAndroidIntegration.beginBootstrap()
    
    22
    +            else -> {}
    
    23
    +        }
    
    24
    +    }
    
    25
    +
    
    10 26
     }

  • mobile/android/fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml
    ... ... @@ -45,6 +45,7 @@
    45 45
             android:layout_marginStart="8dp"
    
    46 46
             android:layout_marginTop="8dp"
    
    47 47
             android:visibility="invisible"
    
    48
    +        android:contentDescription="@string/connection_assist_back_button_content_description_start_again"
    
    48 49
             app:layout_constraintStart_toStartOf="parent"
    
    49 50
             app:layout_constraintTop_toTopOf="parent">
    
    50 51
     
    

  • mobile/android/fenix/app/src/main/res/values/colors.xml
    ... ... @@ -380,6 +380,8 @@
    380 380
     
    
    381 381
         <!-- Connection Assist -->
    
    382 382
         <color name="connect_button_purple">#9059FF</color>
    
    383
    +    <color name="disabled_connect_button_purple">#5C42A9</color>
    
    384
    +    <color name="disabled_text_gray_purple">#8782A9</color>
    
    383 385
         <color name="configure_connection_button_white">#E1E0E7</color>
    
    384 386
         <color name="warning_yellow">#FFA436</color>
    
    385 387
         <color name="progress_background_tint">#55148C</color>
    

  • mobile/android/fenix/app/src/main/res/values/torbrowser_strings.xml
    ... ... @@ -78,7 +78,9 @@
    78 78
         <!-- Connection assist. -->
    
    79 79
         <string name="connection_assist_unblock_the_internet_in_country_or_region">Unblock the internet in:</string>
    
    80 80
         <!-- Connection assist. -->
    
    81
    -    <string name="connection_assist_share_my_location_country_or_region">Share my location</string>
    
    81
    +    <string name="connection_assist_automatic_country_detection">Automatic</string>
    
    82
    +    <!-- Connection assist, -->
    
    83
    +    <string name="connection_assist_select_country_or_region">Select country or region</string>
    
    82 84
         <!-- Connection assist. -->
    
    83 85
         <string name="connection_assist_try_a_bridge_button">Try a bridge</string>
    
    84 86
     
    
    ... ... @@ -122,5 +124,7 @@
    122 124
         <string name="connection_assist_connect_to_tor_before_opening_links">Connect to Tor before opening links</string>
    
    123 125
         <!-- Connection assist. Confirmation button for a shown "Snackbar" (special popup notification). -->
    
    124 126
         <string name="connection_assist_connect_to_tor_before_opening_links_confirmation">CONNECT</string>
    
    127
    +    <!-- Connection assist. Content Description for back button. Button will start the connection assist process again -->
    
    128
    +    <string name="connection_assist_back_button_content_description_start_again">Start again</string>
    
    125 129
     
    
    126 130
     </resources>

  • mobile/android/geckoview/src/main/java/org/mozilla/geckoview/TorAndroidIntegration.java
    ... ... @@ -10,6 +10,8 @@ import android.content.Context;
    10 10
     import android.os.AsyncTask;
    
    11 11
     import android.util.Log;
    
    12 12
     import androidx.annotation.NonNull;
    
    13
    +import androidx.lifecycle.LiveData;
    
    14
    +import androidx.lifecycle.MutableLiveData;
    
    13 15
     import java.io.BufferedReader;
    
    14 16
     import java.io.File;
    
    15 17
     import java.io.FileOutputStream;
    
    ... ... @@ -53,8 +55,10 @@ public class TorAndroidIntegration implements BundleEventListener {
    53 55
       private static final String EVENT_BOOTSTRAP_BEGIN_AUTO = "GeckoView:Tor:BootstrapBeginAuto";
    
    54 56
       private static final String EVENT_BOOTSTRAP_CANCEL = "GeckoView:Tor:BootstrapCancel";
    
    55 57
       private static final String EVENT_BOOTSTRAP_GET_STATE = "GeckoView:Tor:BootstrapGetState";
    
    58
    +  private static final String EVENT_START_AGAIN = "GeckoView:Tor:StartAgain";
    
    56 59
       private static final String EVENT_QUICKSTART_GET = "GeckoView:Tor:QuickstartGet";
    
    57 60
       private static final String EVENT_QUICKSTART_SET = "GeckoView:Tor:QuickstartSet";
    
    61
    +  private static final String EVENT_COUNTRY_NAMES_GET = "GeckoView:Tor:CountryNamesGet";
    
    58 62
     
    
    59 63
       private static final String CONTROL_PORT_FILE = "/control-ipc";
    
    60 64
       private static final String SOCKS_FILE = "/socks-ipc";
    
    ... ... @@ -82,6 +86,9 @@ public class TorAndroidIntegration implements BundleEventListener {
    82 86
     
    
    83 87
       private int mMeekCounter;
    
    84 88
     
    
    89
    +  private final MutableLiveData<TorConnectStage> _lastKnownStage = new MutableLiveData<>(null);
    
    90
    +  public LiveData<TorConnectStage> lastKnowStage = _lastKnownStage;
    
    91
    +
    
    85 92
       /**
    
    86 93
        * mSettings is a Java-side copy of the authoritative settings in the JS code. It's useful to
    
    87 94
        * 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 {
    154 161
           }
    
    155 162
         } else if (EVENT_CONNECT_STAGE_CHANGED.equals(event)) {
    
    156 163
           TorConnectStage stage = new TorConnectStage(message.getBundle("stage"));
    
    164
    +      _lastKnownStage.setValue(stage);
    
    157 165
           for (BootstrapStateChangeListener listener : mBootstrapStateListeners) {
    
    158 166
             listener.onBootstrapStageChange(stage);
    
    159 167
           }
    
    ... ... @@ -693,6 +701,10 @@ public class TorAndroidIntegration implements BundleEventListener {
    693 701
         return EventDispatcher.getInstance().queryVoid(EVENT_SETTINGS_SET, bundle);
    
    694 702
       }
    
    695 703
     
    
    704
    +  public @NonNull GeckoResult<Void> startAgain() {
    
    705
    +    return EventDispatcher.getInstance().queryVoid(EVENT_START_AGAIN);
    
    706
    +  }
    
    707
    +
    
    696 708
       public interface QuickstartGetter {
    
    697 709
         void onValue(boolean enabled);
    
    698 710
       }
    
    ... ... @@ -710,6 +722,17 @@ public class TorAndroidIntegration implements BundleEventListener {
    710 722
         return EventDispatcher.getInstance().queryVoid(EVENT_QUICKSTART_SET, bundle);
    
    711 723
       }
    
    712 724
     
    
    725
    +  public interface CountryNamesGetter {
    
    726
    +    void onValue(GeckoBundle regions);
    
    727
    +  }
    
    728
    +
    
    729
    +  public void countryNamesGet(CountryNamesGetter countryNamesGetter) {
    
    730
    +    EventDispatcher.getInstance().queryBundle(EVENT_COUNTRY_NAMES_GET).then(countryNames -> {
    
    731
    +      countryNamesGetter.onValue(countryNames);
    
    732
    +      return new GeckoResult<Void>();
    
    733
    +    });
    
    734
    +  }
    
    735
    +
    
    713 736
       public @NonNull GeckoResult<Void> beginBootstrap() {
    
    714 737
         return EventDispatcher.getInstance().queryVoid(EVENT_BOOTSTRAP_BEGIN);
    
    715 738
       }
    

  • toolkit/modules/TorAndroidIntegration.sys.mjs
    ... ... @@ -42,8 +42,10 @@ const ListenedEvents = Object.freeze({
    42 42
       bootstrapBeginAuto: "GeckoView:Tor:BootstrapBeginAuto",
    
    43 43
       bootstrapCancel: "GeckoView:Tor:BootstrapCancel",
    
    44 44
       bootstrapGetState: "GeckoView:Tor:BootstrapGetState",
    
    45
    +  startAgain: "GeckoView:Tor:StartAgain",
    
    45 46
       quickstartGet: "GeckoView:Tor:QuickstartGet",
    
    46 47
       quickstartSet: "GeckoView:Tor:QuickstartSet",
    
    48
    +  countryNamesGet: "GeckoView:Tor:CountryNamesGet",
    
    47 49
     });
    
    48 50
     
    
    49 51
     class TorAndroidIntegrationImpl {
    
    ... ... @@ -190,14 +192,18 @@ class TorAndroidIntegrationImpl {
    190 192
             case ListenedEvents.bootstrapGetState:
    
    191 193
               callback?.onSuccess(lazy.TorConnect.state);
    
    192 194
               return;
    
    193
    -        // TODO: Expose TorConnect.startAgain() to allow users to begin
    
    194
    -        // from the start again.
    
    195
    +        case ListenedEvents.startAgain:
    
    196
    +          lazy.TorConnect.startAgain();
    
    197
    +          break;
    
    195 198
             case ListenedEvents.quickstartGet:
    
    196 199
               callback?.onSuccess(lazy.TorConnect.quickstart);
    
    197 200
               return;
    
    198 201
             case ListenedEvents.quickstartSet:
    
    199 202
               lazy.TorConnect.quickstart = data.enabled;
    
    200 203
               break;
    
    204
    +        case ListenedEvents.countryNamesGet:
    
    205
    +          callback?.onSuccess(lazy.TorConnect.countryNames);
    
    206
    +          return;
    
    201 207
           }
    
    202 208
           callback?.onSuccess();
    
    203 209
         } catch (e) {