mirror of
https://github.com/schwabe/ics-openvpn.git
synced 2024-09-20 03:52:27 +02:00
Implement support for setting proxies
This commit is contained in:
parent
c5b13bee94
commit
18ccd98389
@ -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;
|
||||
|
||||
|
@ -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;
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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) {
|
||||
String proto = args[1];
|
||||
if (proto.equals("UDP")) {
|
||||
proxyaddr = null;
|
||||
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 (proxyaddr instanceof InetSocketAddress) {
|
||||
InetSocketAddress isa = (InetSocketAddress) proxyaddr;
|
||||
|
||||
VpnStatus.logInfo(R.string.using_proxy, isa.getHostName(), isa.getPort());
|
||||
if (args.length >= 2 && proxyType == Connection.ProxyType.HTTP) {
|
||||
String proto = args[1];
|
||||
if (proto.equals("UDP")) {
|
||||
proxyname = null;
|
||||
VpnStatus.logInfo("Not using an HTTP proxy since the connection uses UDP");
|
||||
}
|
||||
}
|
||||
|
||||
String proxycmd = String.format(Locale.ENGLISH, "proxy HTTP %s %d\n", 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");
|
||||
}
|
||||
|
||||
@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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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" />
|
||||
|
||||
|
@ -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>
|
||||
|
Loading…
Reference in New Issue
Block a user