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