diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f4576c9da..5f3fd055b 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -80,10 +80,6 @@
android:value=".OrbotActivity"/>
-
-
ClipboardUtils.copyToClipboard("bridge_url", URL_TOR_BRIDGES, getString(R.string.done), this));
-
- String bridges = Prefs.getBridgesList().trim();
- if (!Prefs.bridgesEnabled() || userHasSetPreconfiguredBridge(bridges)) {
- bridges = null;
- }
-
- mEtPastedBridges = findViewById(R.id.etPastedBridges);
- mEtPastedBridges.setOnTouchListener((v, event) -> {
- if (v.hasFocus()) {
- v.getParent().requestDisallowInterceptTouchEvent(true);
- if ((event.getAction() & MotionEvent.ACTION_MASK) == MotionEvent.ACTION_SCROLL) {
- v.getParent().requestDisallowInterceptTouchEvent(false);
- return true;
- }
- }
- return false;
- });
-
- mEtPastedBridges.setText(bridges);
- mEtPastedBridges.addTextChangedListener(this);
- final IntentIntegrator integrator = new IntentIntegrator(this);
-
- findViewById(R.id.btScanQr).setOnClickListener(v -> integrator.initiateScan());
- findViewById(R.id.btShareQr).setOnClickListener(v -> {
- String setBridges = Prefs.getBridgesList();
- if (!TextUtils.isEmpty(setBridges)) {
- try {
- integrator.shareText("bridge://" + URLEncoder.encode(setBridges, "UTF-8"));
- } catch (UnsupportedEncodingException e) {
- e.printStackTrace();
- }
- }
- });
- findViewById(R.id.btEmail).setOnClickListener(v -> {
- String requestText = "get transport";
- String emailUrl = String.format("mailto: %s?subject=%s&body=%s" ,
- Uri.encode(EMAIL_TOR_BRIDGES),
- Uri.encode(requestText),
- Uri.encode(requestText));
- Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse(emailUrl));
- emailIntent.putExtra(Intent.EXTRA_SUBJECT, requestText);
- emailIntent.putExtra(Intent.EXTRA_TEXT, requestText);
- startActivity(Intent.createChooser(emailIntent, getString(R.string.send_email)));
- });
- }
-
- @Override
- public boolean onOptionsItemSelected(@NonNull MenuItem item) {
- if (item.getItemId() == android.R.id.home) {
- finish();
- return true;
- }
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- protected void onActivityResult(int request, int response, Intent data) {
- super.onActivityResult(request, response, data);
-
- IntentResult scanResult = IntentIntegrator.parseActivityResult(request, response, data);
-
- if (scanResult != null) {
- String results = scanResult.getContents();
-
- if (!TextUtils.isEmpty(results)) {
- try {
-
- int urlIdx = results.indexOf("://");
-
- if (urlIdx != -1) {
- results = URLDecoder.decode(results, DEFAULT_ENCODING);
- results = results.substring(urlIdx + 3);
-
- setNewBridges(results);
- } else {
- JSONArray bridgeJson = new JSONArray(results);
- StringBuilder bridgeLines = new StringBuilder();
-
- for (int i = 0; i < bridgeJson.length(); i++) {
- String bridgeLine = bridgeJson.getString(i);
- bridgeLines.append(bridgeLine).append("\n");
- }
-
- setNewBridges(bridgeLines.toString());
- }
- } catch (Exception e) {
- Log.e(getClass().getSimpleName(), "unsupported", e);
- }
- }
-
- setResult(RESULT_OK);
- }
- }
-
- @Override
- public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
- // Ignored.
- }
-
- @Override
- public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
- // Ignored.
- }
-
- @Override
- public void afterTextChanged(Editable editable) {
- setNewBridges(editable.toString(), false);
- }
-
- private void setNewBridges(String newBridgeValue) {
- setNewBridges(newBridgeValue, true);
- }
-
- private void setNewBridges(String bridges, boolean updateEditText) {
- if (bridges != null) {
- bridges = bridges.trim();
-
- if (TextUtils.isEmpty(bridges)) {
- bridges = null;
- }
- }
-
- if (updateEditText) {
- mEtPastedBridges.setText(bridges);
- }
-
- Prefs.setBridgesList(bridges);
- Prefs.putBridgesEnabled(bridges != null);
-
- Intent intent = new Intent(this, OrbotService.class);
- intent.setAction(TorControlCommands.SIGNAL_RELOAD);
- startService(intent);
- }
-
- private static boolean userHasSetPreconfiguredBridge(String bridges) {
- if (bridges == null) return false;
- return bridges.equals("obfs4") || bridges.equals("meek") || bridges.equals("snowflake") || bridges.equals("snowflake-amp");
- }
-}
diff --git a/app/src/main/java/org/torproject/android/ui/onboarding/CustomBridgesFragment.kt b/app/src/main/java/org/torproject/android/ui/onboarding/CustomBridgesFragment.kt
new file mode 100644
index 000000000..6d9ab19e7
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/onboarding/CustomBridgesFragment.kt
@@ -0,0 +1,170 @@
+package org.torproject.android.ui.onboarding
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.content.Intent
+import android.net.Uri
+import android.os.Bundle
+import android.text.Editable
+import android.text.TextWatcher
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.MenuItem
+import android.view.MotionEvent
+import android.view.View
+import android.view.ViewGroup
+import android.widget.EditText
+import android.widget.TextView
+
+import androidx.appcompat.widget.Toolbar
+import androidx.fragment.app.Fragment
+
+import com.google.zxing.integration.android.IntentIntegrator
+
+import net.freehaven.tor.control.TorControlCommands
+
+import org.json.JSONArray
+import org.torproject.android.R
+import org.torproject.android.core.ClipboardUtils.copyToClipboard
+import org.torproject.android.service.OrbotService
+import org.torproject.android.service.util.Prefs
+
+import java.io.UnsupportedEncodingException
+import java.net.URLDecoder
+import java.net.URLEncoder
+import java.nio.charset.StandardCharsets
+
+class CustomBridgesFragment : Fragment(), TextWatcher {
+ private var mEtPastedBridges: EditText? = null
+
+ @SuppressLint("ClickableViewAccessibility")
+ override fun onCreateView(
+ inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?
+ ): View? {
+ val view = inflater.inflate(R.layout.fragment_custom_bridges, container, false)
+
+ val toolbar = view.findViewById(R.id.toolbar)
+ toolbar.setNavigationOnClickListener { activity?.onBackPressed() }
+
+ view.findViewById(R.id.tvDescription).text = getString(R.string.in_a_browser, URL_TOR_BRIDGES)
+
+ view.findViewById(R.id.btCopyUrl).setOnClickListener {
+ copyToClipboard("bridge_url", URL_TOR_BRIDGES, getString(R.string.done), requireContext())
+ }
+
+ var bridges: String? = Prefs.getBridgesList().trim { it <= ' ' }
+ if (!Prefs.bridgesEnabled() || userHasSetPreconfiguredBridge(bridges)) {
+ bridges = null
+ }
+
+ mEtPastedBridges = view.findViewById(R.id.etPastedBridges)
+ mEtPastedBridges?.setOnTouchListener { v, event ->
+ if (v.hasFocus()) {
+ v.parent.requestDisallowInterceptTouchEvent(true)
+ if ((event.action and MotionEvent.ACTION_MASK) == MotionEvent.ACTION_SCROLL) {
+ v.parent.requestDisallowInterceptTouchEvent(false)
+ return@setOnTouchListener true
+ }
+ }
+ false
+ }
+
+ mEtPastedBridges?.setText(bridges)
+ mEtPastedBridges?.addTextChangedListener(this)
+ val integrator = IntentIntegrator(requireActivity())
+
+ view.findViewById(R.id.btScanQr).setOnClickListener { integrator.initiateScan() }
+ view.findViewById(R.id.btShareQr).setOnClickListener {
+ Prefs.getBridgesList().takeIf { it.isNotEmpty() }?.let { setBridges ->
+ try {
+ integrator.shareText("bridge://${URLEncoder.encode(setBridges, "UTF-8")}")
+ } catch (e: UnsupportedEncodingException) {
+ e.printStackTrace()
+ }
+ }
+ }
+ view.findViewById(R.id.btEmail).setOnClickListener {
+ val requestText = "get transport"
+ val emailUrl = "mailto:${Uri.encode(EMAIL_TOR_BRIDGES)}?subject=${Uri.encode(requestText)}&body=${Uri.encode(requestText)}"
+ val emailIntent = Intent(Intent.ACTION_SENDTO, Uri.parse(emailUrl)).apply {
+ putExtra(Intent.EXTRA_SUBJECT, requestText)
+ putExtra(Intent.EXTRA_TEXT, requestText)
+ }
+ startActivity(Intent.createChooser(emailIntent, getString(R.string.send_email)))
+ }
+
+ return view
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home) {
+ activity?.onBackPressed()
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ override fun onActivityResult(request: Int, response: Int, data: Intent?) {
+ super.onActivityResult(request, response, data)
+
+ val scanResult = IntentIntegrator.parseActivityResult(request, response, data)
+ scanResult?.contents?.let { results ->
+ if (results.isNotEmpty()) {
+ try {
+ val urlIdx = results.indexOf("://")
+ val decodedResults = if (urlIdx != -1) {
+ URLDecoder.decode(results, DEFAULT_ENCODING).substring(urlIdx + 3)
+ } else {
+ JSONArray(results).let { bridgeJson ->
+ StringBuilder().apply {
+ for (i in 0 until bridgeJson.length()) {
+ append(bridgeJson.getString(i)).append("\n")
+ }
+ }.toString()
+ }
+ }
+ setNewBridges(decodedResults)
+ } catch (e: Exception) {
+ Log.e(javaClass.simpleName, "unsupported", e)
+ }
+ }
+ activity?.setResult(Activity.RESULT_OK)
+ }
+ }
+
+ override fun beforeTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { /* no-op */ }
+
+ override fun onTextChanged(charSequence: CharSequence, i: Int, i1: Int, i2: Int) { /* no-op */ }
+
+ override fun afterTextChanged(editable: Editable) {
+ setNewBridges(editable.toString(), false)
+ }
+
+ private fun setNewBridges(bridges: String?, updateEditText: Boolean = true) {
+ val trimmedBridges = bridges?.trim()?.takeIf { it.isNotEmpty() }
+
+ if (updateEditText) {
+ mEtPastedBridges?.setText(trimmedBridges)
+ }
+
+ Prefs.setBridgesList(trimmedBridges)
+ Prefs.putBridgesEnabled(trimmedBridges != null)
+
+ Intent(requireContext(), OrbotService::class.java).apply {
+ action = TorControlCommands.SIGNAL_RELOAD
+ requireContext().startService(this)
+ }
+ }
+
+ companion object {
+ private val DEFAULT_ENCODING = StandardCharsets.UTF_8.name()
+ private const val EMAIL_TOR_BRIDGES = "bridges@torproject.org"
+ private const val URL_TOR_BRIDGES = "https://bridges.torproject.org/bridges"
+
+ private fun userHasSetPreconfiguredBridge(bridges: String?): Boolean {
+ return bridges in listOf("obfs4", "meek", "snowflake", "snowflake-amp")
+ }
+ }
+}
diff --git a/app/src/main/res/layout/activity_custom_bridges.xml b/app/src/main/res/layout/fragment_custom_bridges.xml
similarity index 98%
rename from app/src/main/res/layout/activity_custom_bridges.xml
rename to app/src/main/res/layout/fragment_custom_bridges.xml
index b19e57104..8e2f65917 100644
--- a/app/src/main/res/layout/activity_custom_bridges.xml
+++ b/app/src/main/res/layout/fragment_custom_bridges.xml
@@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/dark_purple"
- tools:context=".ui.onboarding.CustomBridgesActivity">
+ tools:context=".ui.onboarding.CustomBridgesFragment">
püchunta sülatia sünainje torproject.org
sülatia wanewaii
sükümajia sülatia wanewaii
-
+
paapa tü sülatiakat wanewai
sünain wane navegador , pünala%sy toque \"einjatüin sülatia\">\"¡shiyane papüinjatka sülatia!\"
pü´ntirera sülatia
diff --git a/app/src/main/res/values-gum/strings.xml b/app/src/main/res/values-gum/strings.xml
index a99a85c16..16530dc68 100644
--- a/app/src/main/res/values-gum/strings.xml
+++ b/app/src/main/res/values-gum/strings.xml
@@ -181,7 +181,7 @@ Trek tamaramikwane asha mar
torproject.org piuntsik teilkan miamik
Neteelɵ Piuntsikmera
Na isuikmei piuntsik teiklan tamaramik
-
+
Piuntsikmera teiklan naaship tamara pɵnsramik
Kan wampirishipiku, \"piuntsikmera\" chipiku kepintrɵ %s> \"piuntsikmerantɵ trantra!\"
Pasta piuntsik teiklɵ
diff --git a/app/src/main/res/values-nah/strings.xml b/app/src/main/res/values-nah/strings.xml
index efdb72ea5..b7d901f9c 100644
--- a/app/src/main/res/values-nah/strings.xml
+++ b/app/src/main/res/values-nah/strings.xml
@@ -181,7 +181,7 @@ atl oquitlapatic
Ipan torprojet.org xictlani in cuauhpanco
Neuhyantica cuauhpanco
Quitlayocoya neuhyantica cuauhpanco
-
+
Xicchihua neuhyantica cuauhpanco
Ipan ce panoliztli, quitetlapalo %s ihuan xicmahpilhuia \"Quipia cuauhpanco\" > \"¡ Zan xinechnamaca cuauhpanco!\"
Quitzaloa in cuauhpanco
diff --git a/app/src/main/res/values-pbb/strings.xml b/app/src/main/res/values-pbb/strings.xml
index fbe03e573..23be2e736 100644
--- a/app/src/main/res/values-pbb/strings.xml
+++ b/app/src/main/res/values-pbb/strings.xml
@@ -180,7 +180,7 @@ Notificaciones ampliadas
Wehtxi’ ayte mpẽhy torproject.org
Idx weh.
Idx wehtx Mphuse\'j
-
+
Idx wehtx miikseelpi’j
Internette pakwenxite, na’s %s mtheg jĩiçxa ayte miikxtheth \"weh muy\" > \"¡wehçxaatx mah!\"
Wehtx miikne’ta’j
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index e2d43b55b..7d47c98d7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -188,7 +188,7 @@
Request Bridges from torproject.org
Custom Bridges
Configure Custom Bridges
-
+
Use Custom Bridges
In a browser, visit %s and tap "Get Bridges" > "Just Give Me Bridges!"
Paste Bridges