summaryrefslogtreecommitdiff
path: root/main
diff options
context:
space:
mode:
authorArne Schwabe <arne@rfc2549.org>2018-03-05 00:20:46 +0100
committerArne Schwabe <arne@rfc2549.org>2018-03-05 01:15:20 +0100
commit18ccd98389bbe25dedb7b2e78b4b1d37f6ed928e (patch)
treee20a27f41ff2fb3552f58d706ee765d95a1971ff /main
parentc5b13bee941e0c7600555ed71ac2d073834bdce6 (diff)
Implement support for setting proxies
Diffstat (limited to 'main')
-rw-r--r--main/src/main/java/de/blinkt/openvpn/VpnProfile.java9
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/Connection.java10
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java101
-rw-r--r--main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java90
-rw-r--r--main/src/main/java/de/blinkt/openvpn/fragments/ConnectionsAdapter.java366
-rw-r--r--main/src/main/res/layout/server_card.xml117
-rwxr-xr-xmain/src/main/res/values/strings.xml5
7 files changed, 486 insertions, 212 deletions
diff --git a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
index 76bb502e..3ee293dc 100644
--- a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
+++ b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java
@@ -61,7 +61,7 @@ public class VpnProfile implements Serializable, Cloneable {
private static final long serialVersionUID = 7085688938959334563L;
public static final int MAXLOGLEVEL = 4;
- public static final int CURRENT_PROFILE_VERSION = 6;
+ public static final int CURRENT_PROFILE_VERSION = 7;
public static final int DEFAULT_MSSFIX_SIZE = 1280;
public static String DEFAULT_DNS1 = "8.8.8.8";
public static String DEFAULT_DNS2 = "8.8.4.4";
@@ -244,6 +244,7 @@ public class VpnProfile implements Serializable, Cloneable {
}
if (mAllowedAppsVpn == null)
mAllowedAppsVpn = new HashSet<>();
+
if (mConnections == null)
mConnections = new Connection[0];
@@ -251,7 +252,11 @@ public class VpnProfile implements Serializable, Cloneable {
if (TextUtils.isEmpty(mProfileCreator))
mUserEditable = true;
}
-
+ if (mProfileVersion < 7) {
+ for (Connection c: mConnections)
+ if (c.mProxyType == null)
+ c.mProxyType = Connection.ProxyType.NONE;
+ }
mProfileVersion = CURRENT_PROFILE_VERSION;
diff --git a/main/src/main/java/de/blinkt/openvpn/core/Connection.java b/main/src/main/java/de/blinkt/openvpn/core/Connection.java
index ff15daec..748455ec 100644
--- a/main/src/main/java/de/blinkt/openvpn/core/Connection.java
+++ b/main/src/main/java/de/blinkt/openvpn/core/Connection.java
@@ -19,6 +19,16 @@ public class Connection implements Serializable, Cloneable {
public boolean mEnabled = true;
public int mConnectTimeout = 0;
public static final int CONNECTION_DEFAULT_TIMEOUT = 120;
+ public ProxyType mProxyType = ProxyType.NONE;
+ public String mProxyName = "proxy.example.com";
+ public String mProxyPort = "8080";
+
+ public enum ProxyType {
+ NONE,
+ HTTP,
+ SOCKS5,
+ ORBOT
+ }
private static final long serialVersionUID = 92031902903829089L;
diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
index c79a0b99..fd11a911 100644
--- a/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
+++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVPNService.java
@@ -15,7 +15,6 @@ import android.app.UiModeManager;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.content.pm.ShortcutManager;
import android.content.res.Configuration;
@@ -47,7 +46,6 @@ import java.util.Collection;
import java.util.Locale;
import java.util.Vector;
-import de.blinkt.openvpn.BuildConfig;
import de.blinkt.openvpn.LaunchVPN;
import de.blinkt.openvpn.R;
import de.blinkt.openvpn.VpnProfile;
@@ -58,8 +56,6 @@ import de.blinkt.openvpn.core.VpnStatus.ByteCountListener;
import de.blinkt.openvpn.core.VpnStatus.StateListener;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTED;
-import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_CONNECTING_NO_SERVER_REPLY_YET;
-import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_START;
import static de.blinkt.openvpn.core.ConnectionStatus.LEVEL_WAITING_FOR_USER_INPUT;
import static de.blinkt.openvpn.core.NetworkSpace.ipAddress;
@@ -68,17 +64,22 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
public static final String START_SERVICE_STICKY = "de.blinkt.openvpn.START_SERVICE_STICKY";
public static final String ALWAYS_SHOW_NOTIFICATION = "de.blinkt.openvpn.NOTIFICATION_ALWAYS_VISIBLE";
public static final String DISCONNECT_VPN = "de.blinkt.openvpn.DISCONNECT_VPN";
- private static final String PAUSE_VPN = "de.blinkt.openvpn.PAUSE_VPN";
- private static final String RESUME_VPN = "de.blinkt.openvpn.RESUME_VPN";
public static final String NOTIFICATION_CHANNEL_BG_ID = "openvpn_bg";
public static final String NOTIFICATION_CHANNEL_NEWSTATUS_ID = "openvpn_newstat";
public static final String VPNSERVICE_TUN = "vpnservice-tun";
- private String lastChannel;
-
+ public final static String ORBOT_PACKAGE_NAME = "org.torproject.android";
+ private static final String PAUSE_VPN = "de.blinkt.openvpn.PAUSE_VPN";
+ private static final String RESUME_VPN = "de.blinkt.openvpn.RESUME_VPN";
+ private static final int PRIORITY_MIN = -2;
+ private static final int PRIORITY_DEFAULT = 0;
+ private static final int PRIORITY_MAX = 2;
private static boolean mNotificationAlwaysVisible = false;
+ private static Class mNotificationActivityClass;
private final Vector<String> mDnslist = new Vector<>();
private final NetworkSpace mRoutes = new NetworkSpace();
private final NetworkSpace mRoutesv6 = new NetworkSpace();
+ private final Object mProcessLock = new Object();
+ private String lastChannel;
private Thread mProcessThread = null;
private VpnProfile mProfile;
private String mDomain = null;
@@ -90,19 +91,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
private boolean mStarting = false;
private long mConnecttime;
private OpenVPNManagement mManagement;
- private String mLastTunCfg;
- private String mRemoteGW;
- private final Object mProcessLock = new Object();
- private Handler guiHandler;
- private Toast mlastToast;
- private Runnable mOpenVPNThread;
- private static Class mNotificationActivityClass;
-
- private static final int PRIORITY_MIN = -2;
- private static final int PRIORITY_DEFAULT = 0;
- private static final int PRIORITY_MAX = 2;
-
-
private final IBinder mBinder = new IOpenVPNServiceInternal.Stub() {
@Override
@@ -127,12 +115,11 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
};
-
- @Override
- public void addAllowedExternalApp(String packagename) throws RemoteException {
- ExternalAppDatabase extapps = new ExternalAppDatabase(OpenVPNService.this);
- extapps.addApp(packagename);
- }
+ private String mLastTunCfg;
+ private String mRemoteGW;
+ private Handler guiHandler;
+ private Toast mlastToast;
+ private Runnable mOpenVPNThread;
// From: http://stackoverflow.com/questions/3758606/how-to-convert-byte-size-into-human-readable-format-in-java
public static String humanReadableByteCount(long bytes, boolean speed, Resources res) {
@@ -172,6 +159,21 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
+ /**
+ * Sets the activity which should be opened when tapped on the permanent notification tile.
+ *
+ * @param activityClass The activity class to open
+ */
+ public static void setNotificationActivityClass(Class<? extends Activity> activityClass) {
+ mNotificationActivityClass = activityClass;
+ }
+
+ @Override
+ public void addAllowedExternalApp(String packagename) throws RemoteException {
+ ExternalAppDatabase extapps = new ExternalAppDatabase(OpenVPNService.this);
+ extapps.addApp(packagename);
+ }
+
@Override
public IBinder onBind(Intent intent) {
String action = intent.getAction();
@@ -211,7 +213,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
}
-
private void showNotification(final String msg, String tickerText, @NonNull String channel, long when, ConnectionStatus status) {
NotificationManager mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
int icon = getIconByConnectionStatus(status);
@@ -255,8 +256,8 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
//noinspection NewApi
nbuilder.setChannelId(channel);
if (mProfile != null)
- //noinspection NewApi
- nbuilder.setShortcutId(mProfile.getUUIDString());
+ //noinspection NewApi
+ nbuilder.setShortcutId(mProfile.getUUIDString());
}
@@ -370,15 +371,6 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
- /**
- * Sets the activity which should be opened when tapped on the permanent notification tile.
- *
- * @param activityClass The activity class to open
- */
- public static void setNotificationActivityClass(Class<? extends Activity> activityClass) {
- mNotificationActivityClass = activityClass;
- }
-
PendingIntent getUserInputIntent(String needed) {
Intent intent = new Intent(getApplicationContext(), LaunchVPN.class);
intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
@@ -589,8 +581,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
}
Runnable processThread;
- if (useOpenVPN3)
- {
+ if (useOpenVPN3) {
OpenVPNManagement mOpenVPN3 = instantiateOpenVPN3Core();
processThread = (Runnable) mOpenVPN3;
mManagement = mOpenVPN3;
@@ -905,14 +896,36 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void setAllowedVpnPackages(Builder builder) {
+ boolean profileUsesOrBot = false;
+
+ for (Connection c : mProfile.mConnections) {
+ if (c.mProxyType == Connection.ProxyType.ORBOT)
+ profileUsesOrBot = true;
+ }
+
+ if (profileUsesOrBot)
+ VpnStatus.logDebug("VPN Profile uses at least one server entry with Orbot. Setting up VPN so that OrBot is not redirected over VPN.");
+
+
boolean atLeastOneAllowedApp = false;
+
+ if (mProfile.mAllowedAppsVpnAreDisallowed && profileUsesOrBot) {
+ try {
+ builder.addDisallowedApplication(ORBOT_PACKAGE_NAME);
+ } catch (PackageManager.NameNotFoundException e) {
+ VpnStatus.logDebug("Orbot not installed?");
+ }
+ }
+
for (String pkg : mProfile.mAllowedAppsVpn) {
try {
if (mProfile.mAllowedAppsVpnAreDisallowed) {
builder.addDisallowedApplication(pkg);
} else {
- builder.addAllowedApplication(pkg);
- atLeastOneAllowedApp = true;
+ if (!(profileUsesOrBot && pkg.equals(ORBOT_PACKAGE_NAME))) {
+ builder.addAllowedApplication(pkg);
+ atLeastOneAllowedApp = true;
+ }
}
} catch (PackageManager.NameNotFoundException e) {
mProfile.mAllowedAppsVpn.remove(pkg);
@@ -1051,7 +1064,7 @@ public class OpenVPNService extends VpnService implements StateListener, Callbac
if (mLocalIP.len <= 31 && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
CIDRIP interfaceRoute = new CIDRIP(mLocalIP.mIp, mLocalIP.len);
interfaceRoute.normalise();
- addRoute(interfaceRoute ,true);
+ addRoute(interfaceRoute, true);
}
diff --git a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
index 096982f9..5285b42f 100644
--- a/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
+++ b/main/src/main/java/de/blinkt/openvpn/core/OpenVpnManagementThread.java
@@ -6,6 +6,7 @@
package de.blinkt.openvpn.core;
import android.content.Context;
+import android.content.Intent;
import android.net.LocalServerSocket;
import android.net.LocalSocket;
import android.net.LocalSocketAddress;
@@ -375,22 +376,94 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
private void processProxyCMD(String argument) {
String[] args = argument.split(",", 3);
- SocketAddress proxyaddr = ProxyDetection.detectProxy(mProfile);
+ Connection.ProxyType proxyType = Connection.ProxyType.NONE;
- if (args.length >= 2) {
+ int connectionEntryNumber = Integer.parseInt(args[0]) - 1;
+ String proxyport = null;
+ String proxyname = null;
+
+ if (mProfile.mConnections.length > connectionEntryNumber) {
+ Connection connection = mProfile.mConnections[connectionEntryNumber];
+ proxyType = connection.mProxyType;
+ proxyname = connection.mProxyName;
+ proxyport = connection.mProxyPort;
+ } else {
+ VpnStatus.logError(String.format(Locale.ENGLISH, "OpenVPN is asking for a proxy of an unknonwn connection entry (%d)", connectionEntryNumber));
+ }
+
+ // atuo detection of proxy
+ if (proxyType == Connection.ProxyType.NONE) {
+ SocketAddress proxyaddr = ProxyDetection.detectProxy(mProfile);
+ if (proxyaddr instanceof InetSocketAddress) {
+ InetSocketAddress isa = (InetSocketAddress) proxyaddr;
+ proxyType = Connection.ProxyType.HTTP;
+ proxyname = isa.getHostName();
+ proxyport = String.valueOf(isa.getPort());
+ }
+ }
+
+
+ if (args.length >= 2 && proxyType == Connection.ProxyType.HTTP) {
String proto = args[1];
if (proto.equals("UDP")) {
- proxyaddr = null;
+ proxyname = null;
+ VpnStatus.logInfo("Not using an HTTP proxy since the connection uses UDP");
}
}
- if (proxyaddr instanceof InetSocketAddress) {
- InetSocketAddress isa = (InetSocketAddress) proxyaddr;
- VpnStatus.logInfo(R.string.using_proxy, isa.getHostName(), isa.getPort());
+ if (proxyType == Connection.ProxyType.ORBOT) {
+ // schwabe: TODO WIP and does not really work
+ /* OrbotHelper orbotHelper = OrbotHelper.get(mOpenVPNService);
+ orbotHelper.addStatusCallback(new StatusCallback() {
+ @Override
+ public void onEnabled(Intent statusIntent) {
+ VpnStatus.logDebug("Orbot onEnabled:" + statusIntent.toString());
+ }
+
+ @Override
+ public void onStarting() {
+ VpnStatus.logDebug("Orbot onStarting");
+ }
+
+ @Override
+ public void onStopping() {
+ VpnStatus.logDebug("Orbot onStopping");
+ }
- String proxycmd = String.format(Locale.ENGLISH, "proxy HTTP %s %d\n", isa.getHostName(), isa.getPort());
+ @Override
+ public void onDisabled() {
+ VpnStatus.logDebug("Orbot onDisabled");
+ }
+
+ @Override
+ public void onStatusTimeout() {
+ VpnStatus.logDebug("Orbot onStatusTimeout");
+ }
+
+ @Override
+ public void onNotYetInstalled() {
+ VpnStatus.logDebug("Orbot notyetinstalled");
+ }
+ });
+ orbotHelper.init();
+ if(!OrbotHelper.requestStartTor(mOpenVPNService))
+
+ VpnStatus.logError("Request starting Orbot failed.");
+ */
+ proxyname = "127.0.0.1";
+ proxyport = "8118";
+ proxyType = Connection.ProxyType.HTTP;
+
+ }
+ if (proxyType != Connection.ProxyType.NONE && proxyname != null) {
+
+ VpnStatus.logInfo(R.string.using_proxy, proxyname, proxyport);
+
+ String proxycmd = String.format(Locale.ENGLISH, "proxy %s %s %s\n",
+ proxyType == Connection.ProxyType.HTTP ? "HTTP" : "SOCKS",
+ proxyname, proxyport);
managmentCommand(proxycmd);
} else {
managmentCommand("proxy NONE\n");
@@ -547,8 +620,9 @@ public class OpenVpnManagementThread implements Runnable, OpenVPNManagement {
try {
// Ignore Auth token message, already managed by openvpn itself
- if (argument.startsWith("Auth-Token:"))
+ if (argument.startsWith("Auth-Token:")) {
return;
+ }
int p1 = argument.indexOf('\'');
int p2 = argument.indexOf('\'', p1 + 1);
diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/ConnectionsAdapter.java b/main/src/main/java/de/blinkt/openvpn/fragments/ConnectionsAdapter.java
index af071d37..bb930eaf 100644
--- a/main/src/main/java/de/blinkt/openvpn/fragments/ConnectionsAdapter.java
+++ b/main/src/main/java/de/blinkt/openvpn/fragments/ConnectionsAdapter.java
@@ -7,7 +7,6 @@ package de.blinkt.openvpn.fragments;
import android.app.AlertDialog;
import android.content.Context;
-import android.content.DialogInterface;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextWatcher;
@@ -15,7 +14,6 @@ import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
-import android.widget.CompoundButton;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.RadioGroup;
@@ -29,14 +27,13 @@ import de.blinkt.openvpn.VpnProfile;
import de.blinkt.openvpn.core.Connection;
public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.ConnectionsHolder> {
+ private static final int TYPE_NORMAL = 0;
+ private static final int TYPE_FOOTER = TYPE_NORMAL + 1;
private final Context mContext;
private final VpnProfile mProfile;
private final Settings_Connections mConnectionFragment;
private Connection[] mConnections;
- private static final int TYPE_NORMAL = 0;
- private static final int TYPE_FOOTER = TYPE_NORMAL + 1;
-
ConnectionsAdapter(Context c, Settings_Connections connections_fragments, VpnProfile vpnProfile) {
mContext = c;
mConnections = vpnProfile.mConnections;
@@ -44,6 +41,134 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
mConnectionFragment = connections_fragments;
}
+ @Override
+ public ConnectionsAdapter.ConnectionsHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
+ LayoutInflater li = LayoutInflater.from(mContext);
+
+ View card;
+ if (viewType == TYPE_NORMAL) {
+ card = li.inflate(R.layout.server_card, viewGroup, false);
+
+ } else { // TYPE_FOOTER
+ card = li.inflate(R.layout.server_footer, viewGroup, false);
+ }
+ return new ConnectionsHolder(card, this, viewType);
+
+ }
+
+ @Override
+ public void onBindViewHolder(final ConnectionsAdapter.ConnectionsHolder cH, int position) {
+ if (position == mConnections.length) {
+ // Footer
+ return;
+ }
+ final Connection connection = mConnections[position];
+
+ cH.mConnection = null;
+
+ cH.mPortNumberView.setText(connection.mServerPort);
+ cH.mServerNameView.setText(connection.mServerName);
+ cH.mPortNumberView.setText(connection.mServerPort);
+ cH.mRemoteSwitch.setChecked(connection.mEnabled);
+
+
+ cH.mProxyNameView.setText(connection.mProxyName);
+ cH.mProxyPortNumberView.setText(connection.mProxyPort);
+
+ cH.mConnectText.setText(String.valueOf(connection.getTimeout()));
+
+ cH.mConnectSlider.setProgress(connection.getTimeout());
+
+
+ cH.mProtoGroup.check(connection.mUseUdp ? R.id.udp_proto : R.id.tcp_proto);
+
+ switch (connection.mProxyType) {
+ case NONE:
+ cH.mProxyGroup.check(R.id.proxy_none);
+ break;
+ case HTTP:
+ cH.mProxyGroup.check(R.id.proxy_http);
+ break;
+ case SOCKS5:
+ cH.mProxyGroup.check(R.id.proxy_http);
+ break;
+ case ORBOT:
+ cH.mProxyGroup.check(R.id.proxy_orbot);
+ break;
+ }
+
+ cH.mCustomOptionsLayout.setVisibility(connection.mUseCustomConfig ? View.VISIBLE : View.GONE);
+ cH.mCustomOptionText.setText(connection.mCustomConfiguration);
+
+ cH.mCustomOptionCB.setChecked(connection.mUseCustomConfig);
+ cH.mConnection = connection;
+
+ setVisibilityProxyServer(cH, connection);
+
+ }
+
+ private void setVisibilityProxyServer(ConnectionsHolder cH, Connection connection) {
+ int visible = (connection.mProxyType == Connection.ProxyType.HTTP || connection.mProxyType == Connection.ProxyType.SOCKS5) ? View.VISIBLE : View.GONE;
+
+ cH.mProxyNameView.setVisibility(visible);
+ cH.mProxyPortNumberView.setVisibility(visible);
+ cH.mProxyNameLabel.setVisibility(visible);
+ }
+
+ private void removeRemote(int idx) {
+ Connection[] mConnections2 = Arrays.copyOf(mConnections, mConnections.length - 1);
+ for (int i = idx + 1; i < mConnections.length; i++) {
+ mConnections2[i - 1] = mConnections[i];
+ }
+ mConnections = mConnections2;
+
+ }
+
+ @Override
+ public int getItemCount() {
+ return mConnections.length + 1; //for footer
+ }
+
+ @Override
+ public int getItemViewType(int position) {
+ if (position == mConnections.length)
+ return TYPE_FOOTER;
+ else
+ return TYPE_NORMAL;
+ }
+
+ void addRemote() {
+ mConnections = Arrays.copyOf(mConnections, mConnections.length + 1);
+ mConnections[mConnections.length - 1] = new Connection();
+ notifyItemInserted(mConnections.length - 1);
+ displayWarningIfNoneEnabled();
+ }
+
+ void displayWarningIfNoneEnabled() {
+ int showWarning = View.VISIBLE;
+ for (Connection conn : mConnections) {
+ if (conn.mEnabled)
+ showWarning = View.GONE;
+ }
+ mConnectionFragment.setWarningVisible(showWarning);
+ }
+
+ void saveProfile() {
+ mProfile.mConnections = mConnections;
+ }
+
+ static abstract class OnTextChangedWatcher implements TextWatcher {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+
+ }
+ }
+
class ConnectionsHolder extends RecyclerView.ViewHolder {
private final EditText mServerNameView;
private final EditText mPortNumberView;
@@ -56,21 +181,31 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
private final EditText mConnectText;
private final SeekBar mConnectSlider;
private final ConnectionsAdapter mConnectionsAdapter;
+ private final RadioGroup mProxyGroup;
+ private final EditText mProxyNameView;
+ private final EditText mProxyPortNumberView;
+ private final View mProxyNameLabel;
private Connection mConnection; // Set to null on update
ConnectionsHolder(View card, ConnectionsAdapter connectionsAdapter, int viewType) {
super(card);
- mServerNameView = (EditText) card.findViewById(R.id.servername);
- mPortNumberView = (EditText) card.findViewById(R.id.portnumber);
- mRemoteSwitch = (Switch) card.findViewById(R.id.remoteSwitch);
- mCustomOptionCB = (CheckBox) card.findViewById(R.id.use_customoptions);
- mCustomOptionText = (EditText) card.findViewById(R.id.customoptions);
- mProtoGroup = (RadioGroup) card.findViewById(R.id.udptcpradiogroup);
+ mServerNameView = card.findViewById(R.id.servername);
+ mPortNumberView = card.findViewById(R.id.portnumber);
+ mRemoteSwitch = card.findViewById(R.id.remoteSwitch);
+ mCustomOptionCB = card.findViewById(R.id.use_customoptions);
+ mCustomOptionText = card.findViewById(R.id.customoptions);
+ mProtoGroup = card.findViewById(R.id.udptcpradiogroup);
mCustomOptionsLayout = card.findViewById(R.id.custom_options_layout);
- mDeleteButton = (ImageButton) card.findViewById(R.id.remove_connection);
- mConnectSlider = (SeekBar) card.findViewById(R.id.connect_silder);
- mConnectText = (EditText) card.findViewById(R.id.connect_timeout);
+ mDeleteButton = card.findViewById(R.id.remove_connection);
+ mConnectSlider = card.findViewById(R.id.connect_silder);
+ mConnectText = card.findViewById(R.id.connect_timeout);
+
+ mProxyGroup = card.findViewById(R.id.proxyradiogroup);
+ mProxyNameView = card.findViewById(R.id.proxyname);
+ mProxyPortNumberView = card.findViewById(R.id.proxyport);
+ mProxyNameLabel = card.findViewById(R.id.proxyserver_label);
+
mConnectionsAdapter = connectionsAdapter;
@@ -80,25 +215,39 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
void addListeners() {
- mRemoteSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mConnection != null) {
- mConnection.mEnabled = isChecked;
- mConnectionsAdapter.displayWarningIfNoneEnabled();
- }
+ mRemoteSwitch.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (mConnection != null) {
+ mConnection.mEnabled = isChecked;
+ mConnectionsAdapter.displayWarningIfNoneEnabled();
}
});
- mProtoGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(RadioGroup group, int checkedId) {
- if (mConnection != null) {
- if (checkedId == R.id.udp_proto)
- mConnection.mUseUdp = true;
- else if (checkedId == R.id.tcp_proto)
- mConnection.mUseUdp = false;
+ mProtoGroup.setOnCheckedChangeListener((group, checkedId) -> {
+ if (mConnection != null) {
+ if (checkedId == R.id.udp_proto)
+ mConnection.mUseUdp = true;
+ else if (checkedId == R.id.tcp_proto)
+ mConnection.mUseUdp = false;
+ }
+ });
+
+ mProxyGroup.setOnCheckedChangeListener((group, checkedId) -> {
+ if (mConnection != null) {
+ switch (checkedId) {
+ case R.id.proxy_none:
+ mConnection.mProxyType = Connection.ProxyType.NONE;
+ break;
+ case R.id.proxy_http:
+ mConnection.mProxyType = Connection.ProxyType.HTTP;
+ break;
+ case R.id.proxy_socks:
+ mConnection.mProxyType = Connection.ProxyType.SOCKS5;
+ break;
+ case R.id.proxy_orbot:
+ mConnection.mProxyType = Connection.ProxyType.ORBOT;
+ break;
}
+ setVisibilityProxyServer(this, mConnection);
}
});
@@ -110,13 +259,10 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
}
});
- mCustomOptionCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- if (mConnection != null) {
- mConnection.mUseCustomConfig = isChecked;
- mCustomOptionsLayout.setVisibility(mConnection.mUseCustomConfig ? View.VISIBLE : View.GONE);
- }
+ mCustomOptionCB.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ if (mConnection != null) {
+ mConnection.mUseCustomConfig = isChecked;
+ mCustomOptionsLayout.setVisibility(mConnection.mUseCustomConfig ? View.VISIBLE : View.GONE);
}
});
@@ -140,6 +286,25 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
}
});
+ mProxyNameView.addTextChangedListener(new OnTextChangedWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (mConnection != null) {
+ mConnection.mProxyName = s.toString();
+ }
+ }
+
+ });
+
+ mProxyPortNumberView.addTextChangedListener(new OnTextChangedWatcher() {
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (mConnection != null) {
+ mConnection.mProxyPort = s.toString();
+ }
+ }
+ });
+
mCustomOptionText.addTextChangedListener(new OnTextChangedWatcher() {
@Override
public void afterTextChanged(Editable s) {
@@ -183,130 +348,19 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
});
mDeleteButton.setOnClickListener(
- new View.OnClickListener() {
- @Override
- public void onClick(View v) {
- AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
- ab.setTitle(R.string.query_delete_remote);
- ab.setPositiveButton(R.string.keep, null);
- ab.setNegativeButton(R.string.delete, new DialogInterface.OnClickListener() {
- @Override
- public void onClick(DialogInterface dialog, int which) {
- removeRemote(getAdapterPosition());
- notifyItemRemoved(getAdapterPosition());
- }
- });
- ab.create().show();
- }
+ v -> {
+ AlertDialog.Builder ab = new AlertDialog.Builder(mContext);
+ ab.setTitle(R.string.query_delete_remote);
+ ab.setPositiveButton(R.string.keep, null);
+ ab.setNegativeButton(R.string.delete, (dialog, which) -> {
+ removeRemote(getAdapterPosition());
+ notifyItemRemoved(getAdapterPosition());
+ });
+ ab.create().show();
}
);
}
-
-
- }
-
-
- @Override
- public ConnectionsAdapter.ConnectionsHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
- LayoutInflater li = LayoutInflater.from(mContext);
-
- View card;
- if (viewType == TYPE_NORMAL) {
- card = li.inflate(R.layout.server_card, viewGroup, false);
-
- } else { // TYPE_FOOTER
- card = li.inflate(R.layout.server_footer, viewGroup, false);
- }
- return new ConnectionsHolder(card, this, viewType);
-
- }
-
- static abstract class OnTextChangedWatcher implements TextWatcher {
- @Override
- public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-
- }
-
- @Override
- public void onTextChanged(CharSequence s, int start, int before, int count) {
-
- }
- }
-
- @Override
- public void onBindViewHolder(final ConnectionsAdapter.ConnectionsHolder cH, int position) {
- if (position == mConnections.length) {
- // Footer
- return;
- }
- final Connection connection = mConnections[position];
-
- cH.mConnection = null;
-
- cH.mPortNumberView.setText(connection.mServerPort);
- cH.mServerNameView.setText(connection.mServerName);
- cH.mPortNumberView.setText(connection.mServerPort);
- cH.mRemoteSwitch.setChecked(connection.mEnabled);
-
-
- cH.mConnectText.setText(String.valueOf(connection.getTimeout()));
-
- cH.mConnectSlider.setProgress(connection.getTimeout());
-
-
- cH.mProtoGroup.check(connection.mUseUdp ? R.id.udp_proto : R.id.tcp_proto);
-
- cH.mCustomOptionsLayout.setVisibility(connection.mUseCustomConfig ? View.VISIBLE : View.GONE);
- cH.mCustomOptionText.setText(connection.mCustomConfiguration);
-
- cH.mCustomOptionCB.setChecked(connection.mUseCustomConfig);
- cH.mConnection = connection;
-
- }
-
-
- private void removeRemote(int idx) {
- Connection[] mConnections2 = Arrays.copyOf(mConnections, mConnections.length - 1);
- for (int i = idx + 1; i < mConnections.length; i++) {
- mConnections2[i - 1] = mConnections[i];
- }
- mConnections = mConnections2;
-
- }
-
- @Override
- public int getItemCount() {
- return mConnections.length + 1; //for footer
- }
-
- @Override
- public int getItemViewType(int position) {
- if (position == mConnections.length)
- return TYPE_FOOTER;
- else
- return TYPE_NORMAL;
- }
-
- void addRemote() {
- mConnections = Arrays.copyOf(mConnections, mConnections.length + 1);
- mConnections[mConnections.length - 1] = new Connection();
- notifyItemInserted(mConnections.length - 1);
- displayWarningIfNoneEnabled();
- }
-
- void displayWarningIfNoneEnabled() {
- int showWarning = View.VISIBLE;
- for (Connection conn : mConnections) {
- if (conn.mEnabled)
- showWarning = View.GONE;
- }
- mConnectionFragment.setWarningVisible(showWarning);
- }
-
-
- void saveProfile() {
- mProfile.mConnections = mConnections;
}
}
diff --git a/main/src/main/res/layout/server_card.xml b/main/src/main/res/layout/server_card.xml
index e24a8fd7..db802468 100644
--- a/main/src/main/res/layout/server_card.xml
+++ b/main/src/main/res/layout/server_card.xml
@@ -154,11 +154,126 @@
android:text="Socks" />
</RadioGroup> -->
+
+ <TextView
+ android:id="@+id/proxy_label"
+ android:layout_below="@id/udptcpradiogroup"
+ android:text="@string/proxy"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ />
+
+ <RadioGroup
+ android:id="@+id/proxyradiogroup"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_below="@id/proxy_label"
+ android:orientation="horizontal"
+ android:paddingStart="20dp"
+ android:paddingEnd="20dp"
+ android:paddingRight="20dp"
+ android:paddingLeft="20dp">
+ <RadioButton
+ android:id="@+id/proxy_none"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/Use_no_proxy" />
+
+ <Space
+ android:layout_width="10dp"
+ android:layout_height="wrap_content" />
+
+ <RadioButton
+ android:id="@+id/proxy_http"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="HTTP"
+ tools:ignore="HardcodedText" />
+
+ <Space
+ android:layout_width="10dp"
+ android:layout_height="wrap_content" />
+
+ <RadioButton
+ android:id="@+id/proxy_socks"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="Socksv5"
+ tools:ignore="HardcodedText" />
+
+ <Space
+ android:layout_width="10dp"
+ android:layout_height="wrap_content" />
+
+ <RadioButton
+ android:visibility="invisible"
+ android:id="@+id/proxy_orbot"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/tor_orbot"
+ />
+ </RadioGroup>
+ <TextView
+ android:id="@+id/proxyport_label"
+ style="@style/item"
+ android:layout_below="@id/proxyradiogroup"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+ android:text="@string/port"
+ android:textAppearance="?android:attr/textAppearanceSmall" />
+
+
+ <TextView
+ android:layout_below="@id/proxyradiogroup"
+ android:layout_toLeftOf="@id/proxyport_label"
+ android:layout_toStartOf="@id/proxyport_label"
+ android:id="@+id/proxyserver_label"
+ android:paddingStart="20dp"
+ android:paddingLeft="20dp"
+ style="@style/item"
+ android:text="@string/address"
+ android:textAppearance="?android:attr/textAppearanceSmall"
+ tools:ignore="RtlSymmetry" />
+
+
+ <EditText
+ android:id="@+id/proxyport"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentEnd="true"
+ android:layout_alignParentRight="true"
+
+ android:layout_below="@id/proxyport_label"
+ android:inputType="numberDecimal"
+ android:text="8080"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
+ tools:ignore="HardcodedText" />
+
+
+ <EditText
+ android:id="@+id/proxyname"
+ android:layout_marginLeft="20dp"
+ android:layout_marginStart="20dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_alignParentLeft="true"
+ android:layout_alignParentStart="true"
+ android:layout_below="@id/proxyserver_label"
+ android:layout_toLeftOf="@id/proxyport"
+ android:layout_toStartOf="@id/proxyport"
+ android:inputType="textUri"
+ android:singleLine="true"
+ tools:text="proxy.blinkt.de"
+ android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium"
+ tools:ignore="HardcodedText" />
+
<TextView
android:id="@+id/connect_timeout_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
- android:layout_below="@id/udptcpradiogroup"
+ android:layout_below="@id/proxyname"
android:paddingTop="10dp"
android:text="@string/connect_timeout" />
diff --git a/main/src/main/res/values/strings.xml b/main/src/main/res/values/strings.xml
index 2c26580a..25adb5a1 100755
--- a/main/src/main/res/values/strings.xml
+++ b/main/src/main/res/values/strings.xml
@@ -199,7 +199,7 @@
<string name="setting_loadtun">Load tun module</string>
<string name="importpkcs12fromconfig">Import PKCS12 from configuration into Android Keystore</string>
<string name="getproxy_error">Error getting proxy settings: %s</string>
- <string name="using_proxy">Using proxy %1$s %2$d</string>
+ <string name="using_proxy">Using proxy %1$s %2$s</string>
<string name="use_system_proxy">Use system proxy</string>
<string name="use_system_proxy_summary">Use the system wide configuration for HTTP/HTTPS proxies to connect.</string>
<string name="onbootrestartsummary">OpenVPN will connect the specified VPN if it was active on system boot. Please read the connection warning FAQ before using this option on Android &lt; 5.0.</string>
@@ -465,5 +465,8 @@
<string name="all_app_prompt">An external app tries to control %s. The app requesting access cannot be determined. Allowing this app grants ALL apps access.</string>
<string name="openvpn3_nostatickeys">The OpenVPN 3 C++ implementation does not support static keys. Please change to OpenVPN 2.x under general settings.</string>
<string name="openvpn3_pkcs12">Using PKCS12 files directly with OpenVPN 3 C++ implementation is not supported. Please import the pkcs12 files into the Android keystore or change to OpenVPN 2.x under general settings.</string>
+ <string name="proxy">Proxy</string>
+ <string name="Use_no_proxy">None</string>
+ <string name="tor_orbot">Tor (Orbot)</string>
</resources>