diff options
author | Arne Schwabe <arne@rfc2549.org> | 2018-03-05 00:20:46 +0100 |
---|---|---|
committer | Arne Schwabe <arne@rfc2549.org> | 2018-03-05 01:15:20 +0100 |
commit | 18ccd98389bbe25dedb7b2e78b4b1d37f6ed928e (patch) | |
tree | e20a27f41ff2fb3552f58d706ee765d95a1971ff /main | |
parent | c5b13bee941e0c7600555ed71ac2d073834bdce6 (diff) |
Implement support for setting proxies
Diffstat (limited to 'main')
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 < 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> |