Commits:
-
333d9284
by clairehurst at 2024-03-26T17:06:53-06:00
fixup! Implement Android-native Connection Assist UI
-
86815386
by clairehurst at 2024-03-26T17:06:54-06:00
fixup! Bug 41878: Add standalone Tor Bootstrap
-
99f95c30
by clairehurst at 2024-03-26T17:06:54-06:00
fixup! Add Tor integration and UI
12 changed files:
Changes:
fenix/app/src/main/java/org/mozilla/fenix/HomeActivity.kt
... |
... |
@@ -168,6 +168,7 @@ import java.util.Locale |
168
|
168
|
import androidx.navigation.fragment.findNavController
|
169
|
169
|
import mozilla.components.browser.engine.gecko.GeckoEngine
|
170
|
170
|
import mozilla.components.browser.state.selector.findCustomTab
|
|
171
|
+import org.mozilla.fenix.home.HomeFragment
|
171
|
172
|
import org.mozilla.geckoview.TorIntegrationAndroid
|
172
|
173
|
|
173
|
174
|
/**
|
... |
... |
@@ -815,6 +816,10 @@ open class HomeActivity : LocaleAwareAppCompatActivity(), NavHostActivity, TorIn |
815
|
816
|
|
816
|
817
|
final override fun onBackPressed() {
|
817
|
818
|
supportFragmentManager.primaryNavigationFragment?.childFragmentManager?.fragments?.forEach {
|
|
819
|
+ if (it is HomeFragment){
|
|
820
|
+ finish()
|
|
821
|
+ return
|
|
822
|
+ }
|
818
|
823
|
if (it is UserInteractionHandler && it.onBackPressed()) {
|
819
|
824
|
return
|
820
|
825
|
}
|
fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistFragment.kt
... |
... |
@@ -4,11 +4,19 @@ |
4
|
4
|
|
5
|
5
|
package org.mozilla.fenix.tor
|
6
|
6
|
|
|
7
|
+import android.graphics.Color
|
7
|
8
|
import android.os.Build
|
8
|
9
|
import android.os.Bundle
|
|
10
|
+import android.text.SpannableString
|
|
11
|
+import android.text.Spanned
|
|
12
|
+import android.text.TextPaint
|
|
13
|
+import android.text.method.LinkMovementMethod
|
|
14
|
+import android.text.style.ClickableSpan
|
|
15
|
+import android.util.Log
|
9
|
16
|
import android.view.LayoutInflater
|
10
|
17
|
import android.view.View
|
11
|
18
|
import android.view.ViewGroup
|
|
19
|
+import androidx.appcompat.content.res.AppCompatResources
|
12
|
20
|
import androidx.fragment.app.Fragment
|
13
|
21
|
import androidx.fragment.app.viewModels
|
14
|
22
|
import androidx.lifecycle.Lifecycle
|
... |
... |
@@ -22,6 +30,7 @@ import org.mozilla.fenix.ext.hideToolbar |
22
|
30
|
|
23
|
31
|
class TorConnectionAssistFragment : Fragment() {
|
24
|
32
|
|
|
33
|
+ private val TAG = "TorConnectionAssistFrag"
|
25
|
34
|
private var _binding: FragmentTorConnectionAssistBinding? = null
|
26
|
35
|
private val binding get() = _binding!!
|
27
|
36
|
|
... |
... |
@@ -49,13 +58,14 @@ class TorConnectionAssistFragment : Fragment() { |
49
|
58
|
|
50
|
59
|
lifecycleScope.launch {
|
51
|
60
|
repeatOnLifecycle(Lifecycle.State.STARTED) {
|
52
|
|
- viewModel.torConnectState.collect {
|
53
|
|
- when (it) {
|
54
|
|
- TorConnectState.Initial -> showConfiguring()
|
|
61
|
+ viewModel.torConnectState.collect { torConnectState ->
|
|
62
|
+ Log.d(TAG, "torConnectState is ${torConnectState.state}")
|
|
63
|
+ when (torConnectState) {
|
|
64
|
+ TorConnectState.Initial -> showSplash()
|
55
|
65
|
TorConnectState.Configuring -> showConfiguring()
|
56
|
66
|
TorConnectState.AutoBootstrapping -> showBootstrapping()
|
57
|
67
|
TorConnectState.Bootstrapping -> showBootstrapping()
|
58
|
|
- TorConnectState.Error -> TODO()
|
|
68
|
+ TorConnectState.Error -> showError()
|
59
|
69
|
TorConnectState.Bootstrapped -> openHome()
|
60
|
70
|
TorConnectState.Disabled -> openHome()
|
61
|
71
|
}
|
... |
... |
@@ -73,59 +83,302 @@ class TorConnectionAssistFragment : Fragment() { |
73
|
83
|
}
|
74
|
84
|
}
|
75
|
85
|
|
76
|
|
- viewModel.quickconnectToggle().observe(
|
77
|
|
- viewLifecycleOwner
|
|
86
|
+ viewModel.quickstartToggle().observe(
|
|
87
|
+ viewLifecycleOwner,
|
78
|
88
|
) {
|
79
|
|
- binding.quickstartSwitch.isChecked = it
|
|
89
|
+ binding.quickstartSwitch.isChecked = it == true
|
|
90
|
+ }
|
|
91
|
+
|
|
92
|
+ binding.quickstartSwitch.setOnCheckedChangeListener { _, isChecked ->
|
|
93
|
+ viewModel.handleQuickstartChecked(isChecked)
|
80
|
94
|
}
|
|
95
|
+ }
|
81
|
96
|
|
|
97
|
+
|
|
98
|
+ private fun showSplash() {
|
|
99
|
+ binding.torBootstrapProgressBar.visibility = View.GONE
|
|
100
|
+ binding.settingsButton.visibility = View.GONE
|
|
101
|
+ binding.backButton.visibility = View.GONE
|
|
102
|
+ binding.torConnectImage.visibility = View.GONE
|
|
103
|
+ binding.titleLargeTextView.visibility = View.GONE
|
|
104
|
+ binding.titleDescription.visibility = View.GONE
|
|
105
|
+ binding.quickStartDescription.visibility = View.GONE
|
|
106
|
+ binding.quickstartSwitch.visibility = View.GONE
|
|
107
|
+ binding.torBootstrapButton1.visibility = View.GONE
|
|
108
|
+ binding.torBootstrapButton2.visibility = View.GONE
|
|
109
|
+ binding.wordmarkLogo.visibility = View.VISIBLE
|
|
110
|
+ }
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+ private fun showConfiguring() {
|
|
114
|
+ binding.wordmarkLogo.visibility = View.GONE
|
|
115
|
+
|
|
116
|
+ binding.torBootstrapProgressBar.visibility = View.INVISIBLE
|
|
117
|
+ binding.torBootstrapProgressBar.progress = 0
|
|
118
|
+ binding.backButton.visibility = View.INVISIBLE
|
|
119
|
+ binding.settingsButton.visibility = View.VISIBLE
|
82
|
120
|
binding.settingsButton.setOnClickListener {
|
83
|
121
|
viewModel.cancelTorBootstrap()
|
84
|
122
|
openSettings()
|
85
|
123
|
}
|
|
124
|
+ binding.torConnectImage.visibility = View.VISIBLE
|
|
125
|
+ binding.torConnectImage.setImageResource(R.drawable.connect)
|
|
126
|
+ binding.titleLargeTextView.visibility = View.VISIBLE
|
|
127
|
+ binding.titleLargeTextView.text = getString(R.string.connection_assist_tor_connect_title)
|
|
128
|
+ binding.titleDescription.visibility = View.VISIBLE
|
|
129
|
+ binding.titleDescription.text =
|
|
130
|
+ getString(R.string.preferences_tor_network_settings_explanation)
|
|
131
|
+ binding.quickStartDescription.visibility = View.VISIBLE
|
|
132
|
+ binding.quickstartSwitch.visibility = View.VISIBLE
|
|
133
|
+ binding.quickstartSwitch.isChecked = viewModel.quickstartToggle().value == true
|
|
134
|
+
|
|
135
|
+ binding.unblockTheInternetInCountryDescription.visibility = View.GONE
|
|
136
|
+ binding.countryDropDown.visibility = View.GONE
|
86
|
137
|
|
87
|
|
- binding.torBootstrapConnectButton.setOnClickListener {
|
|
138
|
+ binding.torBootstrapButton1.visibility = View.VISIBLE
|
|
139
|
+ binding.torBootstrapButton1.text = getString(R.string.tor_bootstrap_connect)
|
|
140
|
+ binding.torBootstrapButton1.setOnClickListener {
|
88
|
141
|
viewModel.handleConnect(lifecycleScope = lifecycleScope)
|
|
142
|
+ showBootstrapping()
|
89
|
143
|
}
|
90
|
144
|
|
91
|
|
- binding.quickstartSwitch.setOnCheckedChangeListener { _, isChecked ->
|
92
|
|
- viewModel.handleQuickstartChecked(isChecked)
|
|
145
|
+ binding.torBootstrapButton2.visibility = View.VISIBLE
|
|
146
|
+ binding.torBootstrapButton2.text =
|
|
147
|
+ getString(R.string.connection_assist_configure_connection_button)
|
|
148
|
+ binding.torBootstrapButton2.setOnClickListener {
|
|
149
|
+ viewModel.cancelTorBootstrap()
|
|
150
|
+ openTorNetworkSettings()
|
93
|
151
|
}
|
94
|
152
|
}
|
95
|
153
|
|
96
|
|
- private fun showConfiguring() {
|
97
|
|
- binding.torBootstrapConnectButton.visibility = View.VISIBLE
|
98
|
|
- binding.torBootstrapNetworkSettingsButton.text =
|
|
154
|
+ private fun showBootstrapping() {
|
|
155
|
+ binding.wordmarkLogo.visibility = View.GONE
|
|
156
|
+
|
|
157
|
+ binding.torBootstrapProgressBar.visibility = View.VISIBLE
|
|
158
|
+ binding.torBootstrapProgressBar.progress = 0
|
|
159
|
+ binding.backButton.visibility = View.INVISIBLE
|
|
160
|
+ binding.settingsButton.visibility = View.VISIBLE
|
|
161
|
+ binding.settingsButton.setOnClickListener {
|
|
162
|
+ viewModel.cancelTorBootstrap()
|
|
163
|
+ openSettings()
|
|
164
|
+ }
|
|
165
|
+ binding.torConnectImage.visibility = View.VISIBLE
|
|
166
|
+ binding.torConnectImage.setImageResource(R.drawable.connect)
|
|
167
|
+ binding.titleLargeTextView.visibility = View.VISIBLE
|
|
168
|
+ binding.titleLargeTextView.text = getString(R.string.connection_assist_connecting_title)
|
|
169
|
+ binding.titleDescription.visibility = View.VISIBLE
|
|
170
|
+ binding.titleDescription.text =
|
|
171
|
+ getString(R.string.preferences_tor_network_settings_explanation)
|
|
172
|
+ binding.quickstartSwitch.visibility = View.VISIBLE
|
|
173
|
+ binding.quickstartSwitch.isChecked = viewModel.quickstartToggle().value == true
|
|
174
|
+ binding.quickstartSwitch.jumpDrawablesToCurrentState()
|
|
175
|
+ binding.quickStartDescription.visibility = View.VISIBLE
|
|
176
|
+ binding.torBootstrapButton1.visibility = View.INVISIBLE
|
|
177
|
+ binding.torBootstrapButton2.visibility = View.VISIBLE
|
|
178
|
+ binding.torBootstrapButton2.text = getString(R.string.btn_cancel)
|
|
179
|
+ binding.torBootstrapButton2.setOnClickListener { viewModel.cancelTorBootstrap() }
|
|
180
|
+ }
|
|
181
|
+
|
|
182
|
+ private suspend fun showError() {
|
|
183
|
+ viewModel.torError.collect {
|
|
184
|
+ Log.d(
|
|
185
|
+ TAG,
|
|
186
|
+ "TorError: details = ${it?.details ?: "null details"}, message = ${it?.message ?: "null message"}",
|
|
187
|
+ )
|
|
188
|
+ when (viewModel.handleError(it)) {
|
|
189
|
+ ErrorScreen.CantConnectToInternet -> showCantConnectToInternet()
|
|
190
|
+ ErrorScreen.CantConnectToTorDirectly -> showCantConnectToTorDirectly()
|
|
191
|
+ ErrorScreen.WeCouldntFindYourLocation -> showWeCouldntFindYourLocation()
|
|
192
|
+ ErrorScreen.WereStillHavingTroubleConnecting -> showWereStillHavingTroubleConnecting()
|
|
193
|
+ ErrorScreen.WeWerentAbleToConnectAutomatically -> showWeWerentAbleToConnectAutomatically()
|
|
194
|
+ null -> {
|
|
195
|
+ // no op
|
|
196
|
+ Log.d(TAG, "ErrorScreen: null, nothing shown")
|
|
197
|
+ }
|
|
198
|
+ }
|
|
199
|
+ }
|
|
200
|
+ }
|
|
201
|
+
|
|
202
|
+ private fun showCantConnectToInternet() {
|
|
203
|
+ Log.d(TAG, "showCantConnectToInternet()")
|
|
204
|
+ binding.torBootstrapProgressBar.visibility = View.VISIBLE
|
|
205
|
+ binding.torBootstrapProgressBar.progressTintList =
|
|
206
|
+ AppCompatResources.getColorStateList(requireContext(), R.color.warning_yellow)
|
|
207
|
+ binding.torBootstrapProgressBar.progress = 100
|
|
208
|
+
|
|
209
|
+ binding.backButton.visibility = View.VISIBLE
|
|
210
|
+ binding.backButton.setOnClickListener {
|
|
211
|
+ showConfiguring()
|
|
212
|
+ }
|
|
213
|
+
|
|
214
|
+ binding.torConnectImage.setImageResource(R.drawable.globe_broken)
|
|
215
|
+ binding.titleLargeTextView.text = getString(R.string.connection_assist_internet_error_title)
|
|
216
|
+
|
|
217
|
+ val learnMore: String = getString(R.string.connection_assist_internet_error_learn_more)
|
|
218
|
+ val internetErrorDescription: String = getString(
|
|
219
|
+ R.string.connection_assist_internet_error_description,
|
|
220
|
+ learnMore,
|
|
221
|
+ )
|
|
222
|
+ handleDescriptionWithClickable(internetErrorDescription, learnMore)
|
|
223
|
+
|
|
224
|
+ binding.quickStartDescription.visibility = View.GONE
|
|
225
|
+ binding.quickstartSwitch.visibility = View.GONE
|
|
226
|
+
|
|
227
|
+ binding.torBootstrapButton1.visibility = View.VISIBLE
|
|
228
|
+ binding.torBootstrapButton1.text =
|
|
229
|
+ getString(R.string.connection_assist_internet_error_try_again)
|
|
230
|
+ binding.torBootstrapButton1.setOnClickListener {
|
|
231
|
+ showTryingAgain()
|
|
232
|
+ viewModel.handleConnect(lifecycleScope = lifecycleScope)
|
|
233
|
+ }
|
|
234
|
+
|
|
235
|
+ binding.torBootstrapButton2.text =
|
99
|
236
|
getString(R.string.connection_assist_configure_connection_button)
|
100
|
|
- binding.torBootstrapNetworkSettingsButton.setOnClickListener {
|
|
237
|
+ binding.torBootstrapButton2.setOnClickListener {
|
101
|
238
|
openTorNetworkSettings()
|
102
|
239
|
}
|
103
|
240
|
}
|
104
|
241
|
|
105
|
|
- private fun showBootstrapping() {
|
106
|
|
- binding.torBootstrapConnectButton.visibility = View.INVISIBLE
|
107
|
|
- binding.torBootstrapNetworkSettingsButton.text = getString(R.string.btn_cancel)
|
108
|
|
- binding.torBootstrapNetworkSettingsButton.setOnClickListener {
|
|
242
|
+ private fun showTryingAgain() {
|
|
243
|
+ Log.d(TAG, "showTryingAgain()")
|
|
244
|
+ binding.torBootstrapProgressBar.progress = 0
|
|
245
|
+ binding.torBootstrapProgressBar.visibility = View.VISIBLE
|
|
246
|
+ binding.torBootstrapProgressBar.progressTintList = null
|
|
247
|
+ binding.torConnectImage.setImageResource(R.drawable.connect)
|
|
248
|
+ binding.titleLargeTextView.text =
|
|
249
|
+ getString(R.string.connection_assist_trying_again_waiting_title)
|
|
250
|
+
|
|
251
|
+ binding.quickstartSwitch.visibility = View.GONE
|
|
252
|
+ binding.quickStartDescription.visibility = View.GONE
|
|
253
|
+ binding.torBootstrapButton1.visibility = View.INVISIBLE
|
|
254
|
+ binding.torBootstrapButton2.visibility = View.VISIBLE
|
|
255
|
+ binding.torBootstrapButton2.text = getString(R.string.btn_cancel)
|
|
256
|
+ binding.torBootstrapButton2.setOnClickListener {
|
109
|
257
|
viewModel.cancelTorBootstrap()
|
|
258
|
+ showConfiguring()
|
110
|
259
|
}
|
111
|
260
|
}
|
112
|
261
|
|
113
|
|
- private fun openSettings(preferenceToScrollTo: String? = null) {
|
114
|
|
- findNavController().navigate(
|
115
|
|
- TorConnectionAssistFragmentDirections
|
116
|
|
- .actionTorConnectionAssistFragmentToSettingsFragment(preferenceToScrollTo),
|
|
262
|
+ private fun showCantConnectToTorDirectly() {
|
|
263
|
+ Log.d(TAG, "showCantConnectToTorDirectly()")
|
|
264
|
+ binding.torBootstrapProgressBar.visibility = View.VISIBLE
|
|
265
|
+ binding.torBootstrapProgressBar.progressTintList =
|
|
266
|
+ AppCompatResources.getColorStateList(requireContext(), R.color.warning_yellow)
|
|
267
|
+ binding.torBootstrapProgressBar.progress = 100
|
|
268
|
+
|
|
269
|
+ binding.backButton.visibility = View.VISIBLE
|
|
270
|
+ binding.backButton.setOnClickListener {
|
|
271
|
+ showConfiguring()
|
|
272
|
+ }
|
|
273
|
+
|
|
274
|
+ binding.torConnectImage.setImageResource(R.drawable.globe_broken)
|
|
275
|
+ binding.titleLargeTextView.text =
|
|
276
|
+ getString(R.string.connection_assist_cant_connect_to_tor_title)
|
|
277
|
+
|
|
278
|
+ val learnMore: String = getString(R.string.connection_assist_internet_error_learn_more)
|
|
279
|
+ val tryABridge: String = getString(
|
|
280
|
+ R.string.connection_assist_try_a_bridge_description,
|
|
281
|
+ learnMore,
|
117
|
282
|
)
|
|
283
|
+ handleDescriptionWithClickable(tryABridge, learnMore)
|
|
284
|
+
|
|
285
|
+ binding.quickStartDescription.visibility = View.GONE
|
|
286
|
+ binding.quickstartSwitch.visibility = View.GONE
|
|
287
|
+ binding.unblockTheInternetInCountryDescription.visibility = View.VISIBLE
|
|
288
|
+ binding.countryDropDown.visibility = View.VISIBLE
|
|
289
|
+ // TODO implement countryDropDown
|
|
290
|
+
|
|
291
|
+ binding.torBootstrapButton1.visibility = View.VISIBLE
|
|
292
|
+ binding.torBootstrapButton1.text = getString(R.string.connection_assist_try_a_bridge_button)
|
|
293
|
+ binding.torBootstrapButton1.setOnClickListener {
|
|
294
|
+ viewModel.tryABridge()
|
|
295
|
+ showTryingABridge()
|
|
296
|
+ }
|
|
297
|
+ binding.torBootstrapButton2.visibility = View.GONE
|
118
|
298
|
}
|
119
|
299
|
|
120
|
|
- private fun openTorNetworkSettings() {
|
121
|
|
- findNavController().navigate(
|
122
|
|
- TorConnectionAssistFragmentDirections
|
123
|
|
- .actionTorConnectionAssistFragmentToTorNetworkSettings(),
|
|
300
|
+ private fun showTryingABridge() {
|
|
301
|
+ Log.d(TAG, "showTryingABridge()")
|
|
302
|
+ // TODO(Not implemented)
|
|
303
|
+ binding.torBootstrapButton2.setOnClickListener {
|
|
304
|
+ showTryingABridge()
|
|
305
|
+ }
|
|
306
|
+ }
|
|
307
|
+
|
|
308
|
+ private fun showWeCouldntFindYourLocation() {
|
|
309
|
+ Log.d(TAG, "showWeCouldntFindYourLocation()")
|
|
310
|
+ // TODO(Not implemented)
|
|
311
|
+ binding.torBootstrapButton2.setOnClickListener {
|
|
312
|
+ showTryingABridge()
|
|
313
|
+ }
|
|
314
|
+ }
|
|
315
|
+
|
|
316
|
+ private fun showWereStillHavingTroubleConnecting() {
|
|
317
|
+ Log.d(TAG, "showWereStillHavingTroubleConnecting()")
|
|
318
|
+ TODO("Not yet implemented")
|
|
319
|
+ }
|
|
320
|
+
|
|
321
|
+ private fun showTryingOneMoreTime() {
|
|
322
|
+ Log.d(TAG, "showTryingOneMoreTime()")
|
|
323
|
+ TODO("Not yet implemented")
|
|
324
|
+ }
|
|
325
|
+
|
|
326
|
+ private fun showWeWerentAbleToConnectAutomatically() {
|
|
327
|
+ Log.d(TAG, "showWeWerentAbleToConnectAutomatically()")
|
|
328
|
+ TODO("Not yet implemented")
|
|
329
|
+ }
|
|
330
|
+
|
|
331
|
+ private fun showUnknownError() {
|
|
332
|
+ Log.d(TAG, "showUnknownError()")
|
|
333
|
+ TODO("Not yet implemented")
|
|
334
|
+ }
|
|
335
|
+
|
|
336
|
+ /**
|
|
337
|
+ * from https://stackoverflow.com/questions/10696986/how-to-set-the-part-of-the-text-view-is-clickable
|
|
338
|
+ */
|
|
339
|
+ private fun handleDescriptionWithClickable(errorDescription: String, learnMore: String) {
|
|
340
|
+ val errorDescriptionSpannableString = SpannableString(errorDescription)
|
|
341
|
+ val clickableSpan: ClickableSpan = object : ClickableSpan() {
|
|
342
|
+ override fun onClick(textView: View) {
|
|
343
|
+ showLearnMore()
|
|
344
|
+ }
|
|
345
|
+
|
|
346
|
+ override fun updateDrawState(drawState: TextPaint) {
|
|
347
|
+ super.updateDrawState(drawState)
|
|
348
|
+ drawState.isUnderlineText = true
|
|
349
|
+ }
|
|
350
|
+ }
|
|
351
|
+ errorDescriptionSpannableString.setSpan(
|
|
352
|
+ clickableSpan,
|
|
353
|
+ errorDescription.length - learnMore.length,
|
|
354
|
+ errorDescription.length,
|
|
355
|
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE,
|
124
|
356
|
)
|
|
357
|
+ binding.titleDescription.text = errorDescriptionSpannableString
|
|
358
|
+ binding.titleDescription.movementMethod = LinkMovementMethod.getInstance()
|
|
359
|
+ binding.titleDescription.highlightColor = Color.TRANSPARENT
|
|
360
|
+ }
|
|
361
|
+
|
|
362
|
+ private fun showLearnMore() {
|
|
363
|
+ //TODO("Not yet implemented")
|
125
|
364
|
}
|
126
|
365
|
|
127
|
366
|
private fun openHome() {
|
|
367
|
+ Log.d(TAG, "openHome()") //This doesn't seem to be ever called
|
128
|
368
|
findNavController().navigate(TorConnectionAssistFragmentDirections.actionStartupHome())
|
129
|
369
|
}
|
130
|
370
|
|
|
371
|
+ private fun openSettings(preferenceToScrollTo: String? = null) {
|
|
372
|
+ findNavController().navigate(
|
|
373
|
+ TorConnectionAssistFragmentDirections.actionTorConnectionAssistFragmentToSettingsFragment(
|
|
374
|
+ preferenceToScrollTo,
|
|
375
|
+ ),
|
|
376
|
+ )
|
|
377
|
+ }
|
|
378
|
+
|
|
379
|
+ private fun openTorNetworkSettings() {
|
|
380
|
+ findNavController().navigate(
|
|
381
|
+ TorConnectionAssistFragmentDirections.actionTorConnectionAssistFragmentToTorNetworkSettings(),
|
|
382
|
+ )
|
|
383
|
+ }
|
131
|
384
|
} |
fenix/app/src/main/java/org/mozilla/fenix/tor/TorConnectionAssistViewModel.kt
... |
... |
@@ -26,24 +26,30 @@ class TorConnectionAssistViewModel( |
26
|
26
|
private val _torConnectState = MutableStateFlow(TorConnectState.Initial)
|
27
|
27
|
internal val torConnectState: StateFlow<TorConnectState> = _torConnectState
|
28
|
28
|
|
29
|
|
- init {
|
30
|
|
- _torController.registerTorListener(this)
|
31
|
|
- }
|
|
29
|
+ private val _torError = MutableStateFlow(_torController.getLastErrorState())
|
|
30
|
+ internal val torError: StateFlow<TorError?> = _torError
|
32
|
31
|
|
33
|
32
|
private val _progress = MutableLiveData(0)
|
34
|
33
|
fun progress(): LiveData<Int> {
|
35
|
34
|
return _progress
|
36
|
35
|
}
|
37
|
36
|
|
38
|
|
- private val _quickconnectToggle = MutableLiveData(_torController.quickstart)
|
39
|
|
- fun quickconnectToggle(): LiveData<Boolean> {
|
40
|
|
- return _quickconnectToggle
|
|
37
|
+ private val _quickStartToggle = MutableLiveData<Boolean>() // don't initialize with quickstart off the bat
|
|
38
|
+ fun quickstartToggle(): LiveData<Boolean?> {
|
|
39
|
+ _quickStartToggle.value = _torController.quickstart // quickstart isn't ready until torSettings is ready
|
|
40
|
+ return _quickStartToggle
|
|
41
|
+ }
|
|
42
|
+
|
|
43
|
+ init {
|
|
44
|
+ Log.d(TAG, "initiating TorConnectionAssistViewModel")
|
|
45
|
+ _torController.registerTorListener(this)
|
41
|
46
|
}
|
42
|
47
|
|
43
|
48
|
fun handleConnect(
|
44
|
49
|
withDebugLogging: Boolean = false,
|
45
|
50
|
lifecycleScope: LifecycleCoroutineScope? = null,
|
46
|
51
|
) {
|
|
52
|
+ Log.d(TAG, "handleConnect initiatingTorBootstrap with lifecycleScope = $lifecycleScope")
|
47
|
53
|
_torController.initiateTorBootstrap(
|
48
|
54
|
withDebugLogging = withDebugLogging,
|
49
|
55
|
lifecycleScope = lifecycleScope,
|
... |
... |
@@ -52,6 +58,7 @@ class TorConnectionAssistViewModel( |
52
|
58
|
|
53
|
59
|
fun handleQuickstartChecked(checked: Boolean) {
|
54
|
60
|
_torController.quickstart = checked
|
|
61
|
+ _quickStartToggle.value = checked
|
55
|
62
|
}
|
56
|
63
|
|
57
|
64
|
fun cancelTorBootstrap() {
|
... |
... |
@@ -66,7 +73,6 @@ class TorConnectionAssistViewModel( |
66
|
73
|
override fun onTorConnected() {
|
67
|
74
|
Log.d(TAG, "onTorConnected()")
|
68
|
75
|
_torController.unregisterTorListener(this)
|
69
|
|
- _torConnectState.value = _torController.lastKnownStatus
|
70
|
76
|
}
|
71
|
77
|
|
72
|
78
|
override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?) {
|
... |
... |
@@ -75,12 +81,32 @@ class TorConnectionAssistViewModel( |
75
|
81
|
_progress.value = progress.toInt()
|
76
|
82
|
}
|
77
|
83
|
_torConnectState.value = _torController.lastKnownStatus
|
|
84
|
+ _torError.value = _torController.getLastErrorState()
|
78
|
85
|
}
|
79
|
86
|
|
80
|
87
|
override fun onTorStopped() {
|
81
|
88
|
Log.d(TAG, "onTorStopped()")
|
82
|
|
- _progress.value = 0
|
83
|
|
- _torConnectState.value = _torController.lastKnownStatus
|
84
|
89
|
}
|
85
|
90
|
|
|
91
|
+ internal fun handleError(it: TorError?): ErrorScreen? {
|
|
92
|
+ // TODO(Only partly implemented)
|
|
93
|
+ if (it?.message == null){
|
|
94
|
+ return null
|
|
95
|
+ }
|
|
96
|
+ return ErrorScreen.CantConnectToInternet
|
|
97
|
+ }
|
|
98
|
+
|
|
99
|
+ fun tryABridge() {
|
|
100
|
+ // TODO("Try a bridge not enabled")
|
|
101
|
+ // connect to bridge based on country
|
|
102
|
+ // try connecting
|
|
103
|
+ }
|
|
104
|
+}
|
|
105
|
+
|
|
106
|
+internal enum class ErrorScreen {
|
|
107
|
+ CantConnectToInternet,
|
|
108
|
+ CantConnectToTorDirectly,
|
|
109
|
+ WeCouldntFindYourLocation,
|
|
110
|
+ WereStillHavingTroubleConnecting,
|
|
111
|
+ WeWerentAbleToConnectAutomatically,
|
86
|
112
|
} |
fenix/app/src/main/java/org/mozilla/fenix/tor/TorController.kt
... |
... |
@@ -12,6 +12,10 @@ interface TorEvents { |
12
|
12
|
fun onTorStatusUpdate(entry: String?, status: String?, progress: Double? = 0.0)
|
13
|
13
|
fun onTorStopped()
|
14
|
14
|
}
|
|
15
|
+class TorError(
|
|
16
|
+ var message: String,
|
|
17
|
+ var details: String
|
|
18
|
+) { }
|
15
|
19
|
|
16
|
20
|
internal enum class TorStatus(val status: String) {
|
17
|
21
|
OFF("OFF"),
|
... |
... |
@@ -59,6 +63,8 @@ interface TorController: TorEvents { |
59
|
63
|
override fun onTorStatusUpdate(entry: String?, status: String?, progress: Double?)
|
60
|
64
|
override fun onTorStopped()
|
61
|
65
|
|
|
66
|
+ fun getLastErrorState() : TorError?
|
|
67
|
+
|
62
|
68
|
fun registerTorListener(l: TorEvents)
|
63
|
69
|
fun unregisterTorListener(l: TorEvents)
|
64
|
70
|
|
fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerGV.kt
... |
... |
@@ -53,6 +53,7 @@ class TorControllerGV( |
53
|
53
|
private var torListeners = mutableListOf<TorEvents>()
|
54
|
54
|
|
55
|
55
|
internal var lastKnownStatus = TorConnectState.Initial
|
|
56
|
+ internal var lastKnownError: TorError? = null
|
56
|
57
|
private var wasTorBootstrapped = false
|
57
|
58
|
private var isTorRestarting = false
|
58
|
59
|
|
... |
... |
@@ -210,6 +211,7 @@ class TorControllerGV( |
210
|
211
|
|
211
|
212
|
override fun setTorStopped() {
|
212
|
213
|
lastKnownStatus = TorConnectState.Configuring
|
|
214
|
+ onTorStatusUpdate(null, lastKnownStatus.toString(), 0.0)
|
213
|
215
|
onTorStopped()
|
214
|
216
|
}
|
215
|
217
|
|
... |
... |
@@ -227,6 +229,10 @@ class TorControllerGV( |
227
|
229
|
}
|
228
|
230
|
}
|
229
|
231
|
|
|
232
|
+ override fun getLastErrorState() : TorError? {
|
|
233
|
+ return lastKnownError
|
|
234
|
+ }
|
|
235
|
+
|
230
|
236
|
// TorEventsBootstrapStateChangeListener -> (lastKnowStatus, TorEvents)
|
231
|
237
|
// Handle events from GeckoView TorAndroidIntegration and map to TorEvents based events
|
232
|
238
|
// and state for firefox-android (designed for tor-android-service)
|
... |
... |
@@ -263,7 +269,7 @@ class TorControllerGV( |
263
|
269
|
}
|
264
|
270
|
|
265
|
271
|
lastKnownStatus = newState
|
266
|
|
-
|
|
272
|
+ onTorStatusUpdate(null, newStateVal, null)
|
267
|
273
|
}
|
268
|
274
|
|
269
|
275
|
// TorEventsBootstrapStateChangeListener
|
... |
... |
@@ -290,7 +296,7 @@ class TorControllerGV( |
290
|
296
|
|
291
|
297
|
// TorEventsBootstrapStateChangeListener
|
292
|
298
|
override fun onBootstrapError(message: String?, details: String?) {
|
293
|
|
- lastKnownStatus = TorConnectState.Error
|
|
299
|
+ lastKnownError = TorError(message ?: "", details ?: "")
|
294
|
300
|
onBootstrapStateChange(TorConnectState.Error.state)
|
295
|
301
|
}
|
296
|
302
|
|
fenix/app/src/main/java/org/mozilla/fenix/tor/TorControllerTAS.kt
... |
... |
@@ -330,4 +330,10 @@ class TorControllerTAS (private val context: Context): TorController { |
330
|
330
|
companion object {
|
331
|
331
|
const val torServiceResponseTimeout = 5000L
|
332
|
332
|
}
|
|
333
|
+
|
|
334
|
+ // Compat with TorControlGV Stubs
|
|
335
|
+
|
|
336
|
+ override fun getLastErrorState() : TorError? {
|
|
337
|
+ return null;
|
|
338
|
+ }
|
333
|
339
|
} |
fenix/app/src/main/res/drawable/connect_broken.xml
|
1
|
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+ android:width="40dp"
|
|
3
|
+ android:height="40dp"
|
|
4
|
+ android:viewportWidth="40"
|
|
5
|
+ android:viewportHeight="40">
|
|
6
|
+ <group>
|
|
7
|
+ <clip-path
|
|
8
|
+ android:pathData="M0,0h40v40h-40z"/>
|
|
9
|
+ <path
|
|
10
|
+ android:pathData="M8.317,5.337C11.521,2.781 15.582,1.253 19.999,1.253C30.352,1.253 38.745,9.647 38.745,20C38.745,24.418 37.218,28.478 34.662,31.681L32.577,29.597C34.611,26.937 35.819,23.611 35.819,20C35.819,11.26 28.739,4.18 19.999,4.18C16.389,4.18 13.063,5.388 10.401,7.421L8.317,5.337Z"
|
|
11
|
+ android:fillColor="#FBFBFE"/>
|
|
12
|
+ <path
|
|
13
|
+ android:pathData="M5.89,7.656C3.002,10.954 1.252,15.273 1.252,20C1.252,28.967 7.545,36.46 15.959,38.307C16.839,38.5 17.732,38.633 18.652,38.693V24.373C16.785,23.8 15.425,22.06 15.425,20C15.425,19.19 15.635,18.43 16.004,17.771L13.887,15.653C13.013,16.88 12.499,18.38 12.499,20C12.499,22.653 13.879,24.987 15.959,26.32V35.293C9.179,33.513 4.179,27.347 4.179,20C4.179,16.08 5.603,12.493 7.963,9.73L5.89,7.656Z"
|
|
14
|
+ android:fillColor="#FBFBFE"/>
|
|
15
|
+ <path
|
|
16
|
+ android:pathData="M16.399,13.419L18.618,15.638C19.054,15.501 19.517,15.427 19.998,15.427C22.525,15.427 24.572,17.473 24.572,20C24.572,20.481 24.498,20.945 24.36,21.38L26.579,23.599C27.165,22.531 27.498,21.304 27.498,20C27.498,15.86 24.138,12.5 19.998,12.5C18.694,12.5 17.468,12.833 16.399,13.419Z"
|
|
17
|
+ android:fillColor="#FBFBFE"/>
|
|
18
|
+ <path
|
|
19
|
+ android:pathData="M24.349,26.112L22.232,23.995C21.954,24.151 21.658,24.278 21.349,24.373V38.693C22.269,38.633 23.162,38.5 24.042,38.307C27.176,37.619 30.015,36.147 32.345,34.109L30.271,32.034C28.492,33.552 26.372,34.681 24.042,35.293V26.32C24.146,26.253 24.249,26.184 24.349,26.112Z"
|
|
20
|
+ android:fillColor="#FBFBFE"/>
|
|
21
|
+ <path
|
|
22
|
+ android:pathData="M30.653,27.67C32.21,25.514 33.127,22.864 33.127,20C33.127,12.753 27.247,6.873 20,6.873C17.138,6.873 14.488,7.791 12.33,9.348L14.437,11.455C16.037,10.412 17.947,9.807 20,9.807C25.634,9.807 30.194,14.367 30.194,20C30.194,22.051 29.587,23.962 28.544,25.562L30.653,27.67Z"
|
|
23
|
+ android:fillColor="#FBFBFE"/>
|
|
24
|
+ <path
|
|
25
|
+ android:pathData="M26.272,28.037L28.357,30.121C27.095,31.163 25.635,31.973 24.041,32.487V29.36C24.844,29.014 25.593,28.568 26.272,28.037Z"
|
|
26
|
+ android:fillColor="#FBFBFE"/>
|
|
27
|
+ <path
|
|
28
|
+ android:pathData="M11.962,13.727L9.878,11.643C8.001,13.914 6.873,16.826 6.873,20C6.873,25.84 10.686,30.787 15.96,32.487V29.36C12.34,27.8 9.806,24.193 9.806,20C9.806,17.633 10.611,15.457 11.962,13.727Z"
|
|
29
|
+ android:fillColor="#FBFBFE"/>
|
|
30
|
+ <path
|
|
31
|
+ android:pathData="M17.922,19.688L20.311,22.077C20.21,22.092 20.105,22.1 19.999,22.1C18.84,22.1 17.899,21.16 17.899,20C17.899,19.894 17.907,19.79 17.922,19.688Z"
|
|
32
|
+ android:fillColor="#FBFBFE"/>
|
|
33
|
+ <path
|
|
34
|
+ android:pathData="M2.89,4.642L35.228,36.98C35.879,37.632 35.879,38.688 35.228,39.339L35.228,39.339C34.576,39.991 33.52,39.991 32.868,39.339L0.53,7.001C-0.121,6.35 -0.121,5.294 0.53,4.642C1.182,3.991 2.238,3.991 2.89,4.642Z"
|
|
35
|
+ android:fillColor="#FBFBFE"/>
|
|
36
|
+ </group>
|
|
37
|
+</vector> |
fenix/app/src/main/res/drawable/globe_broken.xml
|
1
|
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
|
2
|
+ android:width="40dp"
|
|
3
|
+ android:height="40dp"
|
|
4
|
+ android:viewportWidth="40"
|
|
5
|
+ android:viewportHeight="40">
|
|
6
|
+ <path
|
|
7
|
+ android:pathData="M4.209,1.999L37.355,35.145L35.145,37.355L1.999,4.209L4.209,1.999Z"
|
|
8
|
+ android:fillColor="#FBFBFE"
|
|
9
|
+ android:fillType="evenOdd"/>
|
|
10
|
+ <path
|
|
11
|
+ android:pathData="M7.869,5.703C3.82,9.142 1.25,14.271 1.25,20C1.25,30.03 9.126,38.221 19.031,38.725L19.041,38.733L19.047,38.726C19.363,38.742 19.681,38.75 20,38.75C20.32,38.75 20.638,38.742 20.954,38.726L20.96,38.733L20.97,38.725C26.306,38.453 31.053,35.951 34.297,32.132L32.079,29.913C30.228,32.166 27.759,33.891 24.931,34.831C26.854,32.438 28.243,29.75 29.097,26.931L26.534,24.368C25.642,28.517 23.465,32.438 20,35.474C15.763,31.76 13.451,26.722 13.063,21.563H23.728L20.603,18.438H13.063C13.22,16.35 13.692,14.282 14.479,12.313L12.102,9.936C10.844,12.632 10.12,15.52 9.93,18.438H4.453C4.872,14.209 6.978,10.477 10.087,7.922L7.869,5.703ZM15.069,34.831C11.952,30.951 10.239,26.295 9.93,21.563H4.453C5.07,27.779 9.331,32.924 15.069,34.831Z"
|
|
12
|
+ android:fillColor="#FBFBFE"
|
|
13
|
+ android:fillType="evenOdd"/>
|
|
14
|
+ <path
|
|
15
|
+ android:pathData="M13.678,7.093C14.106,6.433 14.569,5.791 15.069,5.169C14.263,5.437 13.486,5.769 12.744,6.159L10.448,3.863C12.985,2.358 15.907,1.434 19.031,1.275L19.041,1.267L19.047,1.274C19.363,1.258 19.681,1.25 20,1.25C20.32,1.25 20.638,1.258 20.954,1.274L20.96,1.267L20.97,1.275C30.875,1.779 38.75,9.97 38.75,20C38.75,23.489 37.798,26.755 36.138,29.553L33.842,27.257C34.752,25.525 35.346,23.601 35.548,21.563H30.071C30.033,22.146 29.974,22.728 29.893,23.308L25.023,18.438H26.938C26.55,13.278 24.238,8.24 20,4.526C18.361,5.963 17.01,7.598 15.947,9.361L13.678,7.093ZM30.071,18.438H35.548C34.931,12.221 30.67,7.076 24.931,5.169C28.049,9.049 29.762,13.705 30.071,18.438Z"
|
|
16
|
+ android:fillColor="#FBFBFE"
|
|
17
|
+ android:fillType="evenOdd"/>
|
|
18
|
+</vector> |
fenix/app/src/main/res/drawable/tor_bootstrap_background_gradient.xml
... |
... |
@@ -7,9 +7,9 @@ |
7
|
7
|
<shape>
|
8
|
8
|
<gradient
|
9
|
9
|
android:angle="225"
|
10
|
|
- android:startColor="#FF7329A4"
|
11
|
|
- android:centerColor="#FF3A3274"
|
12
|
|
- android:endColor="#FF3A3274"
|
|
10
|
+ android:startColor="@color/backgroundGradientLight"
|
|
11
|
+ android:centerColor="@color/backgroundGradientDark"
|
|
12
|
+ android:endColor="@color/backgroundGradientDark"
|
13
|
13
|
android:type="linear" />
|
14
|
14
|
</shape>
|
15
|
15
|
</item>
|
fenix/app/src/main/res/layout/fragment_tor_connection_assist.xml
... |
... |
@@ -3,29 +3,63 @@ |
3
|
3
|
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
4
|
4
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
5
|
5
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
|
6
|
+ xmlns:tools="http://schemas.android.com/tools"
|
6
|
7
|
android:layout_width="match_parent"
|
7
|
8
|
android:layout_height="match_parent"
|
8
|
|
- android:background="">"@drawable/tor_bootstrap_background_gradient">
|
|
9
|
+ android:background="">"@drawable/tor_bootstrap_background_gradient"
|
|
10
|
+ android:paddingBottom="16dp">
|
9
|
11
|
|
10
|
12
|
<ProgressBar
|
11
|
13
|
android:id="@+id/tor_bootstrap_progress_bar"
|
12
|
14
|
style="?android:attr/progressBarStyleHorizontal"
|
13
|
15
|
android:layout_width="match_parent"
|
14
|
16
|
android:layout_height="6dp"
|
|
17
|
+ android:visibility="invisible"
|
15
|
18
|
app:layout_constraintEnd_toEndOf="parent"
|
16
|
19
|
app:layout_constraintStart_toStartOf="parent"
|
17
|
20
|
app:layout_constraintTop_toTopOf="parent" />
|
18
|
21
|
|
19
|
|
- <ImageView
|
|
22
|
+ <androidx.constraintlayout.widget.ConstraintLayout
|
20
|
23
|
android:id="@+id/settings_button"
|
21
|
|
- android:layout_width="24dp"
|
22
|
|
- android:layout_height="24dp"
|
23
|
|
- android:layout_marginTop="26dp"
|
24
|
|
- android:layout_marginEnd="20dp"
|
25
|
|
- android:contentDescription="@string/settings"
|
|
24
|
+ android:layout_width="48dp"
|
|
25
|
+ android:layout_height="48dp"
|
|
26
|
+ android:layout_marginTop="8dp"
|
|
27
|
+ android:layout_marginEnd="8dp"
|
26
|
28
|
app:layout_constraintEnd_toEndOf="parent"
|
27
|
|
- app:layout_constraintTop_toTopOf="parent"
|
28
|
|
- app:srcCompat="@drawable/mozac_ic_settings" />
|
|
29
|
+ app:layout_constraintTop_toTopOf="parent">
|
|
30
|
+
|
|
31
|
+ <ImageView
|
|
32
|
+ android:layout_width="wrap_content"
|
|
33
|
+ android:layout_height="wrap_content"
|
|
34
|
+ android:contentDescription="@string/settings"
|
|
35
|
+ app:layout_constraintBottom_toBottomOf="parent"
|
|
36
|
+ app:layout_constraintEnd_toEndOf="parent"
|
|
37
|
+ app:layout_constraintStart_toStartOf="parent"
|
|
38
|
+ app:layout_constraintTop_toTopOf="parent"
|
|
39
|
+ app:srcCompat="@drawable/mozac_ic_settings" />
|
|
40
|
+ </androidx.constraintlayout.widget.ConstraintLayout>
|
|
41
|
+
|
|
42
|
+ <androidx.constraintlayout.widget.ConstraintLayout
|
|
43
|
+ android:id="@+id/back_button"
|
|
44
|
+ android:layout_width="48dp"
|
|
45
|
+ android:layout_height="48dp"
|
|
46
|
+ android:layout_marginStart="8dp"
|
|
47
|
+ android:layout_marginTop="8dp"
|
|
48
|
+ android:visibility="invisible"
|
|
49
|
+ app:layout_constraintStart_toStartOf="parent"
|
|
50
|
+ app:layout_constraintTop_toTopOf="parent">
|
|
51
|
+
|
|
52
|
+ <ImageView
|
|
53
|
+ android:layout_width="wrap_content"
|
|
54
|
+ android:layout_height="wrap_content"
|
|
55
|
+ android:contentDescription="@string/settings"
|
|
56
|
+ app:layout_constraintBottom_toBottomOf="parent"
|
|
57
|
+ app:layout_constraintEnd_toEndOf="parent"
|
|
58
|
+ app:layout_constraintStart_toStartOf="parent"
|
|
59
|
+ app:layout_constraintTop_toTopOf="parent"
|
|
60
|
+ app:srcCompat="@drawable/mozac_ic_back" />
|
|
61
|
+ </androidx.constraintlayout.widget.ConstraintLayout>
|
|
62
|
+
|
29
|
63
|
|
30
|
64
|
<ImageView
|
31
|
65
|
android:id="@+id/tor_connect_image"
|
... |
... |
@@ -45,99 +79,139 @@ |
45
|
79
|
android:layout_width="match_parent"
|
46
|
80
|
android:layout_height="wrap_content"
|
47
|
81
|
android:layout_marginStart="24dp"
|
48
|
|
- android:layout_marginTop="24dp"
|
49
|
82
|
android:layout_marginEnd="24dp"
|
50
|
83
|
android:text="@string/connection_assist_tor_connect_title"
|
51
|
84
|
android:textColor="#FBFBFE"
|
52
|
|
- android:textFontWeight="400"
|
53
|
85
|
android:textSize="22sp"
|
|
86
|
+ app:layout_constraintBottom_toBottomOf="parent"
|
54
|
87
|
app:layout_constraintEnd_toEndOf="parent"
|
55
|
88
|
app:layout_constraintStart_toStartOf="parent"
|
56
|
|
- app:layout_constraintTop_toBottomOf="@id/tor_connect_image" />
|
|
89
|
+ app:layout_constraintTop_toBottomOf="@id/tor_connect_image"
|
|
90
|
+ app:layout_constraintVertical_bias="0.03" />
|
57
|
91
|
|
58
|
92
|
<TextView
|
59
|
|
- android:id="@+id/connect_to_tor_description"
|
|
93
|
+ android:id="@+id/title_description"
|
60
|
94
|
android:layout_width="match_parent"
|
61
|
95
|
android:layout_height="wrap_content"
|
62
|
96
|
android:layout_marginStart="24dp"
|
63
|
|
- android:layout_marginTop="16dp"
|
64
|
97
|
android:layout_marginEnd="24dp"
|
|
98
|
+ android:lineSpacingExtra="6dp"
|
65
|
99
|
android:text="@string/preferences_tor_network_settings_explanation"
|
66
|
100
|
android:textColor="#FBFBFE"
|
67
|
|
- android:textFontWeight="400"
|
68
|
101
|
android:textSize="14sp"
|
|
102
|
+ app:layout_constraintBottom_toBottomOf="parent"
|
69
|
103
|
app:layout_constraintEnd_toEndOf="parent"
|
70
|
104
|
app:layout_constraintHorizontal_bias="0.0"
|
71
|
105
|
app:layout_constraintStart_toStartOf="parent"
|
72
|
|
- app:layout_constraintTop_toBottomOf="@id/title_large_text_view" />
|
|
106
|
+ app:layout_constraintTop_toBottomOf="@id/title_large_text_view"
|
|
107
|
+ app:layout_constraintVertical_bias="0.03" />
|
|
108
|
+
|
73
|
109
|
|
74
|
110
|
<TextView
|
75
|
|
- android:id="@+id/connect_automatically"
|
76
|
|
- android:layout_width="wrap_content"
|
|
111
|
+ android:id="@+id/quick_start_description"
|
|
112
|
+ android:layout_width="230dp"
|
77
|
113
|
android:layout_height="wrap_content"
|
78
|
114
|
android:layout_marginStart="24dp"
|
79
|
|
- android:layout_marginTop="24dp"
|
80
|
115
|
android:text="@string/connection_assist_always_connect_automatically_toggle_description"
|
81
|
|
- android:textColor="#80FBFBFE"
|
|
116
|
+ android:textColor="#FBFBFE"
|
82
|
117
|
android:textSize="14sp"
|
83
|
|
- app:layout_constraintBottom_toBottomOf="@+id/quickstart_switch"
|
|
118
|
+ app:layout_constraintBottom_toBottomOf="parent"
|
84
|
119
|
app:layout_constraintStart_toStartOf="parent"
|
85
|
|
- app:layout_constraintTop_toTopOf="@+id/quickstart_switch"
|
86
|
|
- app:layout_constraintVertical_bias="1.25" />
|
|
120
|
+ app:layout_constraintTop_toBottomOf="@+id/title_description"
|
|
121
|
+ app:layout_constraintVertical_bias=".03" />
|
87
|
122
|
|
88
|
123
|
<androidx.appcompat.widget.SwitchCompat
|
89
|
124
|
android:id="@+id/quickstart_switch"
|
90
|
125
|
android:layout_width="wrap_content"
|
91
|
126
|
android:layout_height="wrap_content"
|
92
|
|
- android:layout_marginTop="24dp"
|
|
127
|
+ android:layout_marginStart="100dp"
|
93
|
128
|
android:layout_marginEnd="24dp"
|
94
|
129
|
android:layout_marginBottom="24dp"
|
95
|
|
- android:enabled="false"
|
96
|
130
|
android:gravity="center"
|
97
|
|
- app:thumbTint="#7D6298"
|
|
131
|
+ app:layout_constraintBottom_toBottomOf="parent"
|
98
|
132
|
app:layout_constraintEnd_toEndOf="parent"
|
99
|
|
- app:layout_constraintTop_toBottomOf="@id/connect_to_tor_description"
|
|
133
|
+ app:layout_constraintHorizontal_bias="0"
|
|
134
|
+ app:layout_constraintStart_toEndOf="@+id/quick_start_description"
|
|
135
|
+ app:layout_constraintTop_toBottomOf="@id/title_description"
|
|
136
|
+ app:layout_constraintVertical_bias=".023"
|
100
|
137
|
app:layout_goneMarginEnd="6dp"
|
101
|
138
|
app:layout_goneMarginTop="9dp" />
|
102
|
139
|
|
103
|
|
- <Button
|
104
|
|
- android:id="@+id/tor_bootstrap_connect_button"
|
|
140
|
+ <TextView
|
|
141
|
+ android:id="@+id/unblock_the_internet_in_country_description"
|
|
142
|
+ android:layout_width="match_parent"
|
|
143
|
+ android:layout_height="wrap_content"
|
|
144
|
+ android:layout_marginStart="24dp"
|
|
145
|
+ android:layout_marginTop="24dp"
|
|
146
|
+ android:layout_marginEnd="24dp"
|
|
147
|
+ android:text="@string/connection_assist_unblock_the_internet_in_country_or_region"
|
|
148
|
+ android:textColor="#FBFBFE"
|
|
149
|
+ android:visibility="invisible"
|
|
150
|
+ app:layout_constraintEnd_toEndOf="parent"
|
|
151
|
+ app:layout_constraintStart_toStartOf="parent"
|
|
152
|
+ app:layout_constraintTop_toBottomOf="@id/title_description" />
|
|
153
|
+
|
|
154
|
+ <androidx.appcompat.widget.AppCompatSpinner
|
|
155
|
+ android:id="@+id/country_drop_down"
|
|
156
|
+ style="@style/Widget.AppCompat.Spinner.Underlined"
|
105
|
157
|
android:layout_width="match_parent"
|
106
|
158
|
android:layout_height="wrap_content"
|
107
|
159
|
android:layout_marginStart="24dp"
|
|
160
|
+ android:layout_marginTop="8dp"
|
|
161
|
+ android:layout_marginEnd="24dp"
|
|
162
|
+ android:textColor="#FBFBFE"
|
|
163
|
+ android:tooltipText="@string/connection_assist_share_my_location_country_or_region"
|
|
164
|
+ android:visibility="invisible"
|
|
165
|
+ app:layout_constraintEnd_toEndOf="parent"
|
|
166
|
+ app:layout_constraintStart_toStartOf="parent"
|
|
167
|
+ app:layout_constraintTop_toBottomOf="@id/unblock_the_internet_in_country_description" />
|
|
168
|
+
|
|
169
|
+ <ImageView
|
|
170
|
+ android:id="@+id/wordmarkLogo"
|
|
171
|
+ android:layout_width="160dp"
|
|
172
|
+ android:layout_height="160dp"
|
|
173
|
+ android:src="">"@mipmap/ic_launcher_round"
|
|
174
|
+
|
|
175
|
+ app:layout_constraintBottom_toBottomOf="parent"
|
|
176
|
+ app:layout_constraintEnd_toEndOf="parent"
|
|
177
|
+ app:layout_constraintStart_toStartOf="parent"
|
|
178
|
+ app:layout_constraintTop_toTopOf="parent"
|
|
179
|
+ android:contentDescription="" />
|
|
180
|
+
|
|
181
|
+ <Button
|
|
182
|
+ android:id="@+id/tor_bootstrap_button_1"
|
|
183
|
+ android:layout_width="wrap_content"
|
|
184
|
+ android:layout_height="wrap_content"
|
|
185
|
+ android:layout_marginStart="24dp"
|
108
|
186
|
android:layout_marginEnd="24dp"
|
109
|
187
|
android:layout_marginBottom="8dp"
|
110
|
188
|
android:background="">"@drawable/rounded_corners"
|
111
|
189
|
android:backgroundTint="@color/connect_button_purple"
|
112
|
|
- android:maxWidth="312dp"
|
|
190
|
+ android:minWidth="360dp"
|
113
|
191
|
android:text="@string/tor_bootstrap_connect"
|
114
|
192
|
android:textAllCaps="false"
|
115
|
193
|
android:textColor="#FBFBFE"
|
116
|
|
- android:textFontWeight="500"
|
117
|
194
|
android:textSize="14sp"
|
118
|
195
|
android:textStyle="bold"
|
119
|
|
- app:layout_constraintBottom_toTopOf="@id/tor_bootstrap_network_settings_button"
|
|
196
|
+ app:layout_constraintBottom_toTopOf="@id/tor_bootstrap_button_2"
|
120
|
197
|
app:layout_constraintEnd_toEndOf="parent"
|
121
|
|
- app:layout_constraintHorizontal_bias="0.0"
|
122
|
198
|
app:layout_constraintStart_toStartOf="parent"
|
123
|
199
|
app:layout_constraintTop_toBottomOf="@+id/quickstart_switch"
|
124
|
200
|
app:layout_constraintVertical_bias="1" />
|
125
|
201
|
|
126
|
|
-
|
127
|
202
|
<Button
|
128
|
|
- android:id="@+id/tor_bootstrap_network_settings_button"
|
129
|
|
- android:layout_width="match_parent"
|
|
203
|
+ android:id="@+id/tor_bootstrap_button_2"
|
|
204
|
+ android:layout_width="wrap_content"
|
130
|
205
|
android:layout_height="wrap_content"
|
131
|
206
|
android:layout_marginStart="24dp"
|
132
|
207
|
android:layout_marginEnd="24dp"
|
133
|
|
- android:layout_marginBottom="24dp"
|
|
208
|
+ android:layout_marginBottom="8dp"
|
134
|
209
|
android:background="">"@drawable/rounded_corners"
|
135
|
210
|
android:backgroundTint="@color/configure_connection_button_white"
|
136
|
|
- android:maxWidth="312dp"
|
|
211
|
+ android:minWidth="360dp"
|
137
|
212
|
android:text="@string/connection_assist_configure_connection_button"
|
138
|
213
|
android:textAllCaps="false"
|
139
|
214
|
android:textColor="#15141A"
|
140
|
|
- android:textFontWeight="500"
|
141
|
215
|
android:textSize="14sp"
|
142
|
216
|
android:textStyle="bold"
|
143
|
217
|
app:layout_constraintBottom_toBottomOf="parent"
|
fenix/app/src/main/res/values/colors.xml
... |
... |
@@ -273,6 +273,8 @@ |
273
|
273
|
<color name="sync_disconnected_background_private_theme">#5B5846</color>
|
274
|
274
|
<color name="onboarding_illustration_deselected_private_theme">#99FBFBFE</color>
|
275
|
275
|
<color name="prompt_login_edit_text_cursor_color_private_theme">@color/photonViolet50</color>
|
|
276
|
+ <color name="backgroundGradientDark">#FF3A3274</color>
|
|
277
|
+ <color name="backgroundGradientLight">#FF7329A4</color>
|
276
|
278
|
|
277
|
279
|
<!-- Normal theme colors for light mode -->
|
278
|
280
|
<color name="accent_normal_theme">@color/photonInk20</color>
|
... |
... |
@@ -344,5 +346,6 @@ |
344
|
346
|
<!-- Connection Assist -->
|
345
|
347
|
<color name="connect_button_purple">#9059FF</color>
|
346
|
348
|
<color name="configure_connection_button_white">#E1E0E7</color>
|
|
349
|
+ <color name="warning_yellow">#FFA436</color>
|
347
|
350
|
|
348
|
351
|
</resources> |
fenix/app/src/main/res/values/styles.xml
... |
... |
@@ -12,7 +12,7 @@ |
12
|
12
|
<item name="android:windowAnimationStyle">@style/WindowAnimationTransition</item>
|
13
|
13
|
<item name="android:progressBarStyleHorizontal">@style/progressBarStyleHorizontal</item>
|
14
|
14
|
<item name="android:statusBarColor">@android:color/transparent</item>
|
15
|
|
- <item name="android:windowBackground">@color/fx_mobile_layer_color_1</item>
|
|
15
|
+ <item name="android:windowBackground">@color/backgroundGradientDark</item>
|
16
|
16
|
<item name="android:colorEdgeEffect">@color/accent_normal_theme</item>
|
17
|
17
|
<item name="android:colorAccent">@color/fx_mobile_text_color_primary</item>
|
18
|
18
|
<item name="android:textColorPrimary">@color/state_list_text_color</item>
|
|