[Author Prev][Author Next][Thread Prev][Thread Next][Author Index][Thread Index]
[tor-commits] [orbot/master] Issue #324: Implemented CustomBridgesActivity analogous to Onion Browser iOS.
commit 718bb4216527a6af649d4d821b94f67be0d91ebb
Author: Benjamin Erhart <berhart@xxxxxxxxxxxxxxxxxxx>
Date: Mon May 4 15:04:57 2020 +0200
Issue #324: Implemented CustomBridgesActivity analogous to Onion Browser iOS.
---
app/src/main/AndroidManifest.xml | 1 +
.../java/org/torproject/android/MainConstants.java | 4 +-
.../ui/onboarding/BridgeWizardActivity.java | 90 ++-------
.../ui/onboarding/CustomBridgesActivity.java | 219 +++++++++++++++++++++
.../android/ui/onboarding/MoatActivity.java | 2 +-
.../main/res/layout/activity_custom_bridges.xml | 100 ++++++++++
app/src/main/res/layout/content_bridge_wizard.xml | 20 +-
app/src/main/res/values/strings.xml | 11 +-
8 files changed, 365 insertions(+), 82 deletions(-)
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 011b78c7..a3fbec82 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -108,6 +108,7 @@
<activity android:name=".ui.onboarding.OnboardingActivity" />
<activity android:name=".ui.onboarding.BridgeWizardActivity" />
<activity android:name=".ui.onboarding.MoatActivity" />
+ <activity android:name=".ui.onboarding.CustomBridgesActivity" />
<provider
android:name=".ui.hiddenservices.providers.HSContentProvider"
diff --git a/app/src/main/java/org/torproject/android/MainConstants.java b/app/src/main/java/org/torproject/android/MainConstants.java
index f7f20cbe..06e99861 100644
--- a/app/src/main/java/org/torproject/android/MainConstants.java
+++ b/app/src/main/java/org/torproject/android/MainConstants.java
@@ -8,7 +8,9 @@ public interface MainConstants {
//path to check Tor against
String URL_TOR_CHECK = "https://check.torproject.org";
- String URL_TOR_BRIDGES = "https://bridges.torproject.org/bridges?transport=";
+ String URL_TOR_BRIDGES = "https://bridges.torproject.org/bridges";
+
+ String EMAIL_TOR_BRIDGES = "bridges@xxxxxxxxxxxxxx";
int RESULT_CLOSE_ALL = 0;
diff --git a/app/src/main/java/org/torproject/android/ui/onboarding/BridgeWizardActivity.java b/app/src/main/java/org/torproject/android/ui/onboarding/BridgeWizardActivity.java
index 6b439468..7f7561ba 100644
--- a/app/src/main/java/org/torproject/android/ui/onboarding/BridgeWizardActivity.java
+++ b/app/src/main/java/org/torproject/android/ui/onboarding/BridgeWizardActivity.java
@@ -1,14 +1,11 @@
package org.torproject.android.ui.onboarding;
-import android.app.AlertDialog;
-import android.app.Dialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
-import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.text.TextUtils;
+import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.RadioButton;
@@ -28,8 +25,6 @@ import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;
-import static org.torproject.android.MainConstants.URL_TOR_BRIDGES;
-
public class BridgeWizardActivity extends AppCompatActivity {
private static int MOAT_REQUEST_CODE = 666;
@@ -38,8 +33,7 @@ public class BridgeWizardActivity extends AppCompatActivity {
private RadioButton mBtDirect;
private RadioButton mBtObfs4;
private RadioButton mBtMeek;
- private RadioButton mBtNew;
- private RadioButton mBtMoat;
+ private RadioButton mBtCustom;
@Override
@@ -59,6 +53,14 @@ public class BridgeWizardActivity extends AppCompatActivity {
setTitle(getString(R.string.bridges));
+ findViewById(R.id.btnMoat).setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ startActivityForResult(new Intent(BridgeWizardActivity.this, MoatActivity.class),
+ MOAT_REQUEST_CODE);
+ }
+ });
+
mBtDirect = findViewById(R.id.btnBridgesDirect);
mBtDirect.setOnClickListener(new View.OnClickListener() {
@Override
@@ -91,22 +93,18 @@ public class BridgeWizardActivity extends AppCompatActivity {
});
- mBtNew = findViewById(R.id.btnBridgesNew);
- mBtNew.setOnClickListener(new View.OnClickListener() {
+ mBtCustom = findViewById(R.id.btnCustomBridges);
+ mBtCustom.setOnClickListener(new View.OnClickListener() {
@Override
- public void onClick(View v) {
- showGetBridgePrompt();
+ public void onClick(View view) {
+ startActivity(new Intent(BridgeWizardActivity.this, CustomBridgesActivity.class));
}
});
+ }
- mBtMoat = findViewById(R.id.btnMoat);
- mBtMoat.setOnClickListener(new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- startActivityForResult(new Intent(BridgeWizardActivity.this, MoatActivity.class),
- MOAT_REQUEST_CODE);
- }
- });
+ @Override
+ protected void onResume() {
+ super.onResume();
evaluateBridgeListState();
}
@@ -144,49 +142,6 @@ public class BridgeWizardActivity extends AppCompatActivity {
}
}
- private void showGetBridgePrompt() {
- new AlertDialog.Builder(this)
- .setTitle(R.string.bridge_mode)
- .setMessage(R.string.you_must_get_a_bridge_address_by_email_web_or_from_a_friend_once_you_have_this_address_please_paste_it_into_the_bridges_preference_in_orbot_s_setting_and_restart_)
- .setNegativeButton(R.string.btn_cancel, new Dialog.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- //do nothing
- }
- })
- .setNeutralButton(R.string.get_bridges_email, new Dialog.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- sendGetBridgeEmail();
- }
-
- })
- .setPositiveButton(R.string.get_bridges_web, new Dialog.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- openBrowser(URL_TOR_BRIDGES);
- }
- }).show();
- }
-
- private void sendGetBridgeEmail() {
- String email = "bridges@xxxxxxxxxxxxxx";
- Uri emailUri = Uri.parse("mailto:" + email);
- Intent emailIntent = new Intent(Intent.ACTION_SENDTO, emailUri);
- emailIntent.putExtra(Intent.EXTRA_SUBJECT, "get transport");
- emailIntent.putExtra(Intent.EXTRA_TEXT, "get transport");
- startActivity(Intent.createChooser(emailIntent, getString(R.string.send_email)));
- }
-
-
- /*
- * Launch the system activity for Uri viewing with the provided url
- */
- @SuppressWarnings("SameParameterValue")
- private void openBrowser(final String browserLaunchUrl) {
- startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(browserLaunchUrl)));
- }
-
private void testBridgeConnection() {
if (TextUtils.isEmpty(Prefs.getBridgesList()) || (!Prefs.bridgesEnabled())) {
@@ -258,6 +213,8 @@ public class BridgeWizardActivity extends AppCompatActivity {
}
private void evaluateBridgeListState() {
+ Log.d(getClass().getSimpleName(), String.format("bridgesEnabled=%b, bridgesList=%s", Prefs.bridgesEnabled(), Prefs.getBridgesList()));
+
if (!Prefs.bridgesEnabled()) {
mBtDirect.setChecked(true);
}
@@ -268,12 +225,7 @@ public class BridgeWizardActivity extends AppCompatActivity {
mBtObfs4.setChecked(true);
}
else {
- mBtDirect.setChecked(false);
- mBtMeek.setChecked(false);
- mBtObfs4.setChecked(false);
+ mBtCustom.setChecked(true);
}
-
- mBtNew.setChecked(false);
- mBtMoat.setChecked(false);
}
}
diff --git a/app/src/main/java/org/torproject/android/ui/onboarding/CustomBridgesActivity.java b/app/src/main/java/org/torproject/android/ui/onboarding/CustomBridgesActivity.java
new file mode 100644
index 00000000..0aa0c5ee
--- /dev/null
+++ b/app/src/main/java/org/torproject/android/ui/onboarding/CustomBridgesActivity.java
@@ -0,0 +1,219 @@
+/* Copyright (c) 2020, Benjamin Erhart, Orbot / The Guardian Project - https://guardianproject.info */
+/* See LICENSE for licensing information */
+package org.torproject.android.ui.onboarding;
+
+import android.content.ClipData;
+import android.content.ClipboardManager;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.ActionBar;
+import androidx.appcompat.app.AppCompatActivity;
+import androidx.appcompat.widget.Toolbar;
+
+import com.google.zxing.integration.android.IntentIntegrator;
+import com.google.zxing.integration.android.IntentResult;
+
+import org.json.JSONArray;
+import org.torproject.android.R;
+import org.torproject.android.service.OrbotService;
+import org.torproject.android.service.TorServiceConstants;
+import org.torproject.android.service.util.Prefs;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+
+import static org.torproject.android.MainConstants.EMAIL_TOR_BRIDGES;
+import static org.torproject.android.MainConstants.URL_TOR_BRIDGES;
+
+public class CustomBridgesActivity extends AppCompatActivity implements View.OnClickListener, TextWatcher {
+
+ private EditText mEtPastedBridges;
+
+ @Override
+ public void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_custom_bridges);
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ ActionBar actionBar = getSupportActionBar();
+ if (actionBar != null) {
+ actionBar.setDisplayHomeAsUpEnabled(true);
+ }
+
+ setTitle(getString(R.string.use_custom_bridges));
+
+ ((TextView) findViewById(R.id.tvDescription)).setText(getString(R.string.in_a_browser, URL_TOR_BRIDGES));
+
+ findViewById(R.id.btCopyUrl).setOnClickListener(this);
+
+ String bridges = Prefs.getBridgesList().trim();
+ if (!Prefs.bridgesEnabled() || bridges.equals("obfs4") || bridges.equals("meek")) {
+ bridges = null;
+ }
+
+ mEtPastedBridges = findViewById(R.id.etPastedBridges);
+ mEtPastedBridges.setText(bridges);
+ mEtPastedBridges.addTextChangedListener(this);
+
+ findViewById(R.id.btScanQr).setOnClickListener(this);
+
+ findViewById(R.id.btShareQr).setOnClickListener(this);
+
+ findViewById(R.id.btEmail).setOnClickListener(this);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(@NonNull MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ finish();
+
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onClick(View view) {
+ IntentIntegrator integrator = new IntentIntegrator(this);
+
+ switch (view.getId()) {
+ case R.id.btCopyUrl:
+ ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+
+ if (clipboard != null) {
+ clipboard.setPrimaryClip(ClipData.newPlainText(URL_TOR_BRIDGES, URL_TOR_BRIDGES));
+
+ Toast.makeText(this, R.string.done, Toast.LENGTH_LONG).show();
+ }
+
+ break;
+
+ case R.id.btScanQr:
+ integrator.initiateScan();
+
+ break;
+
+ case R.id.btShareQr:
+ String bridges = Prefs.getBridgesList();
+
+ if (!TextUtils.isEmpty(bridges)) {
+ try {
+ bridges = "bridge://" + URLEncoder.encode(bridges, "UTF-8");
+
+ integrator.shareText(bridges);
+
+ } catch (UnsupportedEncodingException e) {
+ e.printStackTrace();
+ }
+ }
+
+ break;
+
+ case R.id.btEmail:
+ Intent emailIntent = new Intent(Intent.ACTION_SENDTO, Uri.parse("mailto:" + EMAIL_TOR_BRIDGES));
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, "get transport");
+ emailIntent.putExtra(Intent.EXTRA_TEXT, "get transport");
+ startActivity(Intent.createChooser(emailIntent, getString(R.string.send_email)));
+
+ break;
+ }
+ }
+
+ @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, "UTF-8");
+ 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(TorServiceConstants.CMD_SIGNAL_HUP);
+ startService(intent);
+ }
+}
diff --git a/app/src/main/java/org/torproject/android/ui/onboarding/MoatActivity.java b/app/src/main/java/org/torproject/android/ui/onboarding/MoatActivity.java
index 89a20eec..df390a2f 100644
--- a/app/src/main/java/org/torproject/android/ui/onboarding/MoatActivity.java
+++ b/app/src/main/java/org/torproject/android/ui/onboarding/MoatActivity.java
@@ -405,7 +405,7 @@ public class MoatActivity extends AppCompatActivity implements View.OnClickListe
Log.d(MoatActivity.class.getSimpleName(), "Set up Volley queue. host=" + host + ", port=" + port);
- mQueue = Volley.newRequestQueue(MoatActivity.this, new ProxiedHurlStack(host, port));
+ mQueue = Volley.newRequestQueue(this, new ProxiedHurlStack(host, port));
sendIntentToService(TorServiceConstants.CMD_SIGNAL_HUP);
diff --git a/app/src/main/res/layout/activity_custom_bridges.xml b/app/src/main/res/layout/activity_custom_bridges.xml
new file mode 100644
index 00000000..8a7b2998
--- /dev/null
+++ b/app/src/main/res/layout/activity_custom_bridges.xml
@@ -0,0 +1,100 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@color/dark_purple"
+ tools:context=".ui.onboarding.CustomBridgesActivity">
+
+ <com.google.android.material.appbar.AppBarLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:theme="@style/DefaultTheme.AppBarOverlay">
+
+ <androidx.appcompat.widget.Toolbar
+ android:id="@+id/toolbar"
+ android:layout_width="match_parent"
+ android:layout_height="?attr/actionBarSize"
+ android:background="?attr/colorPrimary"
+ app:popupTheme="@style/DefaultTheme.PopupOverlay" />
+
+ </com.google.android.material.appbar.AppBarLayout>
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical" >
+
+ <TextView
+ android:id="@+id/tvDescription"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/in_a_browser" />
+
+ <Button
+ android:id="@+id/btCopyUrl"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/copy_address_to_clipboard" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/paste_bridges" />
+
+ <EditText
+ android:id="@+id/etPastedBridges"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:lines="5"
+ android:inputType="textMultiLine|textNoSuggestions"
+ tools:ignore="Autofill,LabelFor" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/use_qr_code" />
+
+ <Button
+ android:id="@+id/btScanQr"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/menu_scan" />
+
+ <Button
+ android:id="@+id/btShareQr"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/menu_share_bridge" />
+
+ <TextView
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/get_bridges_email" />
+
+ <Button
+ android:id="@+id/btEmail"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/get_bridges_email" />
+
+ </LinearLayout>
+
+ </ScrollView>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/content_bridge_wizard.xml b/app/src/main/res/layout/content_bridge_wizard.xml
index ce8b6e6c..d8e20d82 100644
--- a/app/src/main/res/layout/content_bridge_wizard.xml
+++ b/app/src/main/res/layout/content_bridge_wizard.xml
@@ -19,8 +19,15 @@
android:textSize="16sp"
android:textStyle="bold" />
+ <Button
+ android:id="@+id/btnMoat"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_margin="12dp"
+ android:text="@string/request_bridges_from_torproject" />
+
<RadioGroup
- android:layout_width="wrap_content"
+ android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
@@ -46,18 +53,11 @@
android:text="@string/bridge_cloud" />
<RadioButton
- android:id="@+id/btnBridgesNew"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_margin="12dp"
- android:text="@string/bridges_get_new" />
-
- <RadioButton
- android:id="@+id/btnMoat"
+ android:id="@+id/btnCustomBridges"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="12dp"
- android:text="@string/bridges_get_new_moat"/>
+ android:text="@string/custom_bridges"/>
</RadioGroup>
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 072de5b5..d7139115 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -249,7 +249,6 @@
<string name="bridge_community">Connect through community servers</string>
<string name="bridge_cloud">Connect through cloud servers</string>
<string name="bridges_get_new">Request New Bridges via Email...</string>
- <string name="bridges_get_new_moat">Request New Bridges Directly...</string>
<string name="trouble_connecting">Trouble connecting?</string>
<string name="full_device_vpn">Full Device VPN</string>
@@ -268,4 +267,14 @@
<string name="solve_captcha_instruction">Solve the CAPTCHA to request bridges.</string>
<string name="captcha">Captcha</string>
<string name="enter_characters_from_image">Enter characters from image</string>
+
+ <!-- BridgeWizardActivity -->
+ <string name="request_bridges_from_torproject">Request Bridges from torproject.org</string>
+ <string name="custom_bridges">Custom Bridges</string>
+
+ <!-- CustomBridgesActivity -->
+ <string name="use_custom_bridges">Use Custom Bridges</string>
+ <string name="in_a_browser">In a browser, visit %s and tap "Get Bridges" > "Just Give Me Bridges!"</string>
+ <string name="paste_bridges">Paste Bridges</string>
+ <string name="use_qr_code">Use QR Code</string>
</resources>
_______________________________________________
tor-commits mailing list
tor-commits@xxxxxxxxxxxxxxxxxxxx
https://lists.torproject.org/cgi-bin/mailman/listinfo/tor-commits