diff options
22 files changed, 1112 insertions, 195 deletions
diff --git a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java index 6f21a847..aff2a362 100644 --- a/main/src/main/java/de/blinkt/openvpn/VpnProfile.java +++ b/main/src/main/java/de/blinkt/openvpn/VpnProfile.java @@ -47,6 +47,7 @@ import javax.crypto.Cipher; import javax.crypto.IllegalBlockSizeException; import javax.crypto.NoSuchPaddingException; +import de.blinkt.openvpn.core.Connection; import de.blinkt.openvpn.core.NativeUtils; import de.blinkt.openvpn.core.OpenVPNService; import de.blinkt.openvpn.core.VPNLaunchHelper; @@ -67,7 +68,7 @@ public class VpnProfile implements Serializable { private static final long serialVersionUID = 7085688938959334563L; public static final int MAXLOGLEVEL = 4; - public static final int CURRENT_PROFILE_VERSION = 2; + public static final int CURRENT_PROFILE_VERSION = 4; public static final int DEFAULT_MSSFIX_SIZE = 1450; public static String DEFAULT_DNS1 = "8.8.8.8"; public static String DEFAULT_DNS2 = "8.8.4.4"; @@ -102,12 +103,10 @@ public class VpnProfile implements Serializable { public String mClientKeyFilename; public String mCaFilename; public boolean mUseLzo = true; - public String mServerPort = "1194"; - public boolean mUseUdp = true; public String mPKCS12Filename; public String mPKCS12Password; public boolean mUseTLSAuth = false; - public String mServerName = "openvpn.blinkt.de"; + public String mDNS1 = DEFAULT_DNS1; public String mDNS2 = DEFAULT_DNS2; public String mIPv4Address; @@ -148,6 +147,13 @@ public class VpnProfile implements Serializable { public String mExcludedRoutes; public String mExcludedRoutesv6; public int mMssFix =0; // -1 is default, + public Connection[] mConnections; + public boolean mRemoteRandom=false; + + /* Options no long used in new profiles */ + public String mServerName = "openvpn.blinkt.de"; + public String mServerPort = "1194"; + public boolean mUseUdp = true; @@ -202,7 +208,25 @@ public class VpnProfile implements Serializable { mAllowLocalLAN = Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT; } + + if (mProfileVersion < 4) + moveOptionsToConnection(); + mProfileVersion= CURRENT_PROFILE_VERSION; + + } + + private void moveOptionsToConnection() { + mConnections = new Connection[1]; + Connection conn = new Connection(); + + conn.mServerName = mServerName; + conn.mServerPort = mServerPort; + conn.mUseUdp = mUseUdp; + conn.mCustomConfiguration = ""; + + mConnections[0] = conn; + } public String getConfigFile(Context context, boolean configForOvpn3) { @@ -263,15 +287,19 @@ public class VpnProfile implements Serializable { // We cannot use anything else than tun cfg += "dev tun\n"; - // Server Address - cfg += "remote "; - cfg += mServerName; - cfg += " "; - cfg += mServerPort; - if (mUseUdp) - cfg += " udp\n"; - else - cfg += " tcp-client\n"; + if (mConnections.length==1) { + cfg += mConnections[0].getConnectionBlock(); + } else { + if (mRemoteRandom) + cfg+="remote-random\n"; + for (Connection conn : mConnections) { + if (conn.mEnabled) { + cfg += "<connection>\n"; + cfg += conn.getConnectionBlock(); + cfg += "</connection>\n"; + } + } + } switch (mAuthenticationType) { diff --git a/main/src/main/java/de/blinkt/openvpn/activities/VPNPreferences.java b/main/src/main/java/de/blinkt/openvpn/activities/VPNPreferences.java index 1f022545..1641a51b 100644 --- a/main/src/main/java/de/blinkt/openvpn/activities/VPNPreferences.java +++ b/main/src/main/java/de/blinkt/openvpn/activities/VPNPreferences.java @@ -25,6 +25,7 @@ import de.blinkt.openvpn.core.ProfileManager; import de.blinkt.openvpn.fragments.Settings_Authentication; import de.blinkt.openvpn.fragments.Settings_Basic; import de.blinkt.openvpn.fragments.Settings_Behaviour; +import de.blinkt.openvpn.fragments.Settings_Connections; import de.blinkt.openvpn.fragments.Settings_IP; import de.blinkt.openvpn.fragments.Settings_Obscure; import de.blinkt.openvpn.fragments.Settings_Routing; @@ -37,7 +38,7 @@ public class VPNPreferences extends PreferenceActivity { static final Class validFragments[] = new Class[] { Settings_Authentication.class, Settings_Basic.class, Settings_IP.class, Settings_Obscure.class, Settings_Routing.class, ShowConfigFragment.class, - Settings_Behaviour.class + Settings_Behaviour.class, Settings_Connections.class }; private String mProfileUUID; diff --git a/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java b/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java index 9e99ba55..3015b6fe 100644 --- a/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java +++ b/main/src/main/java/de/blinkt/openvpn/core/ConfigParser.java @@ -5,9 +5,15 @@ package de.blinkt.openvpn.core; +import android.text.TextUtils; +import android.util.Pair; + import java.io.BufferedReader; import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; import java.io.Reader; +import java.io.StringReader; import java.util.Collections; import java.util.HashMap; import java.util.Locale; @@ -28,9 +34,6 @@ public class ConfigParser { private HashMap<String, Vector<Vector<String>>> options = new HashMap<String, Vector<Vector<String>>>(); private HashMap<String, Vector<String>> meta = new HashMap<String, Vector<String>>(); - - private boolean extraRemotesAsCustom=false; - public void parseConfig(Reader reader) throws IOException, ConfigParseError { @@ -53,7 +56,7 @@ public class ConfigParser { meta.put(metaarg.get(0),metaarg); continue; } - Vector<String> args = parseline(line); + Vector<String> args = parseline(line); if(args.size() ==0) continue; @@ -98,7 +101,7 @@ public class ConfigParser { break; else { inlinefile+=line; - inlinefile+= "\n"; + inlinefile+= "\n"; } } while(true); @@ -132,7 +135,7 @@ public class ConfigParser { // adapted openvpn's parse function to java private Vector<String> parseline(String line) throws ConfigParseError { - Vector<String> parameters = new Vector<String>(); + Vector<String> parameters = new Vector<String>(); if (line.length()==0) return parameters; @@ -145,12 +148,12 @@ public class ConfigParser { int pos=0; String currentarg=""; - do { + do { // Emulate the c parsing ... char in; if(pos < line.length()) in = line.charAt(pos); - else + else in = '\0'; if (!backslash && in == '\\' && state != linestate.readin_single_quote) @@ -228,10 +231,7 @@ public class ConfigParser { } - final String[] unsupportedOptions = { "config", - "connection", - "proto-force", - "remote-random", + final String[] unsupportedOptions = { "config", "tls-server" }; @@ -299,7 +299,7 @@ public class ConfigParser { "remote", "float", "port", -// "connect-retry", + "connect-retry", "connect-timeout", "connect-retry-max", "link-mtu", @@ -325,7 +325,7 @@ public class ConfigParser { // This method is far too long @SuppressWarnings("ConstantConditions") - public VpnProfile convertProfile() throws ConfigParseError{ + public VpnProfile convertProfile() throws ConfigParseError, IOException { boolean noauthtypeset=true; VpnProfile np = new VpnProfile(CONVERTED_PROFILE); // Pull, client, tls-client @@ -338,7 +338,7 @@ public class ConfigParser { } Vector<String> secret = getOption("secret", 1, 2); - if(secret!=null) + if(secret!=null) { np.mAuthenticationType=VpnProfile.TYPE_STATICKEYS; noauthtypeset=false; @@ -362,7 +362,7 @@ public class ConfigParser { if (route.size() >= 4) gateway = route.get(3); - String net = route.get(1); + String net = route.get(1); try { CIDRIP cidr = new CIDRIP(net, netmask); if (gateway.equals("net_gateway")) @@ -398,7 +398,7 @@ public class ConfigParser { Vector<Vector<String>> tlsauthoptions = getAllOption("tls-auth", 1, 2); if(tlsauthoptions!=null) { for(Vector<String> tlsauth:tlsauthoptions) { - if(tlsauth!=null) + if(tlsauth!=null) { if(!tlsauth.get(1).equals("[inline]")) { np.mTLSAuthFilename=tlsauth.get(1); @@ -458,36 +458,6 @@ public class ConfigParser { throw new ConfigParseError("Invalid mode for --mode specified, need p2p"); } - Vector<String> port = getOption("port", 1,1); - if(port!=null){ - np.mServerPort = port.get(1); - } - - Vector<String> rport = getOption("rport", 1,1); - if(rport!=null){ - np.mServerPort = rport.get(1); - } - - Vector<String> proto = getOption("proto", 1,1); - if(proto!=null){ - np.mUseUdp=isUdpProto(proto.get(1)); - } - - // Parse remote config - Vector<Vector<String>> remotes = getAllOption("remote",1,3); - - if(remotes!=null && remotes.size()>=1 ) { - Vector<String> remote = remotes.get(0); - switch (remote.size()) { - case 4: - np.mUseUdp=isUdpProto(remote.get(3)); - case 3: - np.mServerPort = remote.get(2); - case 2: - np.mServerName = remote.get(1); - } - } - Vector<Vector<String>> dhcpoptions = getAllOption("dhcp-option", 2, 2); @@ -581,18 +551,18 @@ public class ConfigParser { if(verifyx509name!=null){ np.mRemoteCN = verifyx509name.get(1); np.mCheckRemoteCN=true; - if(verifyx509name.size()>2) { + if(verifyx509name.size()>2) { if (verifyx509name.get(2).equals("name")) np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN; else if (verifyx509name.get(2).equals("name-prefix")) np.mX509AuthType=VpnProfile.X509_VERIFY_TLSREMOTE_RDN_PREFIX; - else + else throw new ConfigParseError("Unknown parameter to x509-verify-name: " + verifyx509name.get(2) ); } else { np.mX509AuthType = VpnProfile.X509_VERIFY_TLSREMOTE_DN; } - } + } Vector<String> verb = getOption("verb",1,1); @@ -615,7 +585,7 @@ public class ConfigParser { if(connectretrymax!=null) np.mConnectRetryMax =connectretrymax.get(1); - Vector<Vector<String>> remotetls = getAllOption("remote-cert-tls", 1, 1); + Vector<Vector<String>> remotetls = getAllOption("remote-cert-tls", 1, 1); if(remotetls!=null) if(remotetls.get(0).get(1).equals("server")) np.mExpectTLSCert=true; @@ -639,7 +609,49 @@ public class ConfigParser { } } - // Parse OpenVPN Access Server extra + Pair<Connection, Connection[]> conns = parseConnectionOptions(null); + np.mConnections =conns.second; + + Vector<Vector<String>> connectionBlocks = getAllOption("connection", 1, 1); + + if (np.mConnections.length > 0 && connectionBlocks !=null ) { + throw new ConfigParseError("Using a <connection> block and --remote is not allowed."); + } + + if (connectionBlocks!=null) { + np.mConnections = new Connection[connectionBlocks.size()]; + + int connIndex = 0; + for (Vector<String> conn : connectionBlocks) { + Pair<Connection, Connection[]> connectionBlockConnection = + parseConnection(conn.get(1), conns.first); + + if (connectionBlockConnection.second.length != 1) + throw new ConfigParseError("A <connection> block must have exactly one remote"); + np.mConnections[connIndex] = connectionBlockConnection.second[0]; + connIndex++; + } + } + if(getOption("remote-random", 0, 0) != null) + np.mRemoteRandom=true; + + Vector<String> protoforce = getOption("proto-force", 1, 1); + if(protoforce!=null) { + boolean disableUDP; + String protoToDisable = protoforce.get(1); + if (protoToDisable.equals("udp")) + disableUDP=true; + else if (protoToDisable.equals("tcp")) + disableUDP=false; + else + throw new ConfigParseError(String.format("Unknown protocol %s in proto-force", protoToDisable)); + + for (Connection conn:np.mConnections) + if(conn.mUseUdp==disableUDP) + conn.mEnabled=false; + } + + // Parse OpenVPN Access Server extra Vector<String> friendlyname = meta.get("FRIENDLY_NAME"); if(friendlyname !=null && friendlyname.size() > 1) np.mName=friendlyname.get(1); @@ -649,20 +661,95 @@ public class ConfigParser { if(ocusername !=null && ocusername.size() > 1) np.mUsername=ocusername.get(1); - // Check the other options - if(remotes !=null && remotes.size()>1 && extraRemotesAsCustom) { - // first is already added - remotes.remove(0); - np.mCustomConfigOptions += getOptionStrings(remotes); - np.mUseCustomConfig=true; - - } - checkIgnoreAndInvalidOptions(np); + checkIgnoreAndInvalidOptions(np); fixup(np); return np; } + private Pair<Connection, Connection[]> parseConnection(String connection, Connection defaultValues) throws IOException, ConfigParseError { + // Parse a connection Block as a new configuration file + + + ConfigParser connectionParser = new ConfigParser(); + StringReader reader = new StringReader(connection.substring(VpnProfile.INLINE_TAG.length())); + connectionParser.parseConfig(reader); + + Pair<Connection, Connection[]> conn = connectionParser.parseConnectionOptions(defaultValues); + + return conn; + } + + private Pair<Connection, Connection[]> parseConnectionOptions(Connection connDefault) throws ConfigParseError { + Connection conn; + if (connDefault!=null) + try { + conn = connDefault.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + return null; + } + else + conn = new Connection(); + + Vector<String> port = getOption("port", 1,1); + if(port!=null){ + conn.mServerPort = port.get(1); + } + + Vector<String> rport = getOption("rport", 1,1); + if(rport!=null){ + conn.mServerPort = rport.get(1); + } + + Vector<String> proto = getOption("proto", 1,1); + if(proto!=null){ + conn.mUseUdp=isUdpProto(proto.get(1)); + } + + + // Parse remote config + Vector<Vector<String>> remotes = getAllOption("remote",1,3); + + + // Assume that we need custom options if connectionDefault are set + if(connDefault!=null) { + for (Vector<Vector<String>> option : options.values()) { + + conn.mCustomConfiguration += getOptionStrings(option); + + } + if (!TextUtils.isEmpty(conn.mCustomConfiguration)) + conn.mUseCustomConfig = true; + } + // Make remotes empty to simplify code + if (remotes==null) + remotes = new Vector<Vector<String>>(); + + Connection[] connections = new Connection[remotes.size()]; + + + int i=0; + for (Vector<String> remote: remotes) { + try { + connections[i] = conn.clone(); + } catch (CloneNotSupportedException e) { + e.printStackTrace(); + } + switch (remote.size()) { + case 4: + connections[i].mUseUdp=isUdpProto(remote.get(3)); + case 3: + connections[i].mServerPort = remote.get(2); + case 2: + connections[i].mServerName = remote.get(1); + } + i++; + } + return Pair.create(conn, connections); + + } + private void checkRedirectParameters(VpnProfile np, Vector<Vector<String>> defgw) { for (Vector<String> redirect: defgw) for (int i=1;i<redirect.size();i++){ @@ -673,20 +760,16 @@ public class ConfigParser { } } - public void useExtraRemotesAsCustom(boolean b) { - this.extraRemotesAsCustom = b; - } - private boolean isUdpProto(String proto) throws ConfigParseError { boolean isudp; if(proto.equals("udp") || proto.equals("udp6")) isudp=true; else if (proto.equals("tcp-client") || - proto.equals("tcp") || + proto.equals("tcp") || proto.equals("tcp6") || proto.endsWith("tcp6-client")) isudp =false; - else + else throw new ConfigParseError("Unsupported option to --proto " + proto); return isudp; } diff --git a/main/src/main/java/de/blinkt/openvpn/core/Connection.java b/main/src/main/java/de/blinkt/openvpn/core/Connection.java new file mode 100644 index 00000000..88cde137 --- /dev/null +++ b/main/src/main/java/de/blinkt/openvpn/core/Connection.java @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2012-2014 Arne Schwabe + * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + */ + +package de.blinkt.openvpn.core; + +import android.text.TextUtils; + +import java.io.Serializable; + +public class Connection implements Serializable, Cloneable { + public String mServerName = "openvpn.blinkt.de"; + public String mServerPort = "1194"; + public boolean mUseUdp = true; + public String mCustomConfiguration=""; + public boolean mUseCustomConfig=false; + public boolean mEnabled=true; + + + public String getConnectionBlock() { + String cfg=""; + + // Server Address + cfg += "remote "; + cfg += mServerName; + cfg += " "; + cfg += mServerPort; + if (mUseUdp) + cfg += " udp\n"; + else + cfg += " tcp-client\n"; + + if (!TextUtils.isEmpty(mCustomConfiguration) && mUseCustomConfig) { + cfg += mCustomConfiguration; + cfg += "\n"; + } + return cfg; + } + + @Override + public Connection clone() throws CloneNotSupportedException { + return (Connection) super.clone(); + } +} diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/ConnectionsAdapter.java b/main/src/main/java/de/blinkt/openvpn/fragments/ConnectionsAdapter.java new file mode 100644 index 00000000..3d20db73 --- /dev/null +++ b/main/src/main/java/de/blinkt/openvpn/fragments/ConnectionsAdapter.java @@ -0,0 +1,176 @@ +/* + * Copyright (c) 2012-2014 Arne Schwabe + * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + */ + +package de.blinkt.openvpn.fragments; + +import android.content.Context; +import android.text.Editable; +import android.text.TextWatcher; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.CheckBox; +import android.widget.CompoundButton; +import android.widget.EditText; +import android.widget.RadioGroup; +import android.widget.Switch; +import android.widget.TextView; + +import java.util.Arrays; + +import de.blinkt.openvpn.R; +import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.Connection; + +/** + * Created by arne on 30.10.14. + */ +public class ConnectionsAdapter extends BaseAdapter { + private final Context mContext; + private final VpnProfile mProfile; + private final Settings_Connections mConnectionFragment; + private Connection[] mConnections; + + public ConnectionsAdapter(Context c, Settings_Connections connections_fragments, VpnProfile vpnProfile) + { + mContext = c; + mConnections = vpnProfile.mConnections; + mProfile = vpnProfile; + mConnectionFragment = connections_fragments; + } + + @Override + public int getCount() { + return mConnections.length; + } + + @Override + public Object getItem(int position) { + return position; + } + + @Override + public long getItemId(int position) { + return 0; + } + + // create a new ImageView for each item referenced by the Adapter + @Override + public View getView(int position, View convertView, ViewGroup parent) { + View card; + if (convertView==null) { + LayoutInflater li = LayoutInflater.from(mContext); + card = li.inflate(R.layout.server_card, parent, false); + } else { + card = convertView; + } + final Connection connection = mConnections[position]; + ((TextView)card.findViewById(R.id.portnumber)). + setText(connection.mServerPort); + + EditText serverNameView = (EditText) card.findViewById(R.id.servername); + EditText portNumberView = (EditText) card.findViewById(R.id.portnumber); + + serverNameView.setText(connection.mServerName); + portNumberView.setText(connection.mServerPort); + + Switch remoteSwitch = (Switch) card.findViewById (R.id.remoteSwitch); + remoteSwitch.setChecked(connection.mEnabled); + remoteSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + connection.mEnabled = isChecked; + displayWarningifNoneEnabled(); + } + }); + + + serverNameView.addTextChangedListener(new 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 afterTextChanged(Editable s) { + connection.mServerName = s.toString(); + } + }); + + portNumberView.addTextChangedListener(new 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 afterTextChanged(Editable s) { + connection.mServerPort = s.toString(); + } + }); + + CheckBox customOptionCB = (CheckBox) card.findViewById(R.id.use_customoptions); + final EditText editText = (EditText) card.findViewById(R.id.customoptions); + RadioGroup protoGroup = (RadioGroup) card.findViewById(R.id.udptcpradiogroup); + protoGroup.check(connection.mUseUdp ? R.id.udp_proto : R.id.tcp_proto); + protoGroup.setOnCheckedChangeListener(new RadioGroup.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(RadioGroup group, int checkedId) { + if (checkedId == R.id.udp_proto) + connection.mUseUdp=true; + else if (checkedId == R.id.tcp_proto) + connection.mUseUdp=false; + } + }); + + final View customOptionsLayout = card.findViewById(R.id.custom_options_layout); + + customOptionsLayout.setVisibility(connection.mUseCustomConfig ? View.VISIBLE : View.GONE); + editText.setText(connection.mCustomConfiguration); + + customOptionCB.setChecked(connection.mUseCustomConfig); + customOptionCB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) + { + connection.mUseCustomConfig = isChecked; + customOptionsLayout.setVisibility(connection.mUseCustomConfig ? View.VISIBLE : View.GONE); + } + }); + displayWarningifNoneEnabled(); + return card; + } + + private void displayWarningifNoneEnabled() { + int showWarning = View.VISIBLE; + for(Connection conn:mConnections) { + if(conn.mEnabled) + showWarning= View.GONE; + } + mConnectionFragment.setWarningVisible(showWarning); + } + + public void addRemote() { + mConnections = Arrays.copyOf(mConnections, mConnections.length+1); + mConnections[mConnections.length-1] = new Connection(); + + notifyDataSetInvalidated(); + } + + public void saveProfile() { + mProfile.mConnections= mConnections; + } +} diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java b/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java index 52e8d784..1fda0a9c 100644 --- a/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java +++ b/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Basic.java @@ -30,7 +30,6 @@ import android.widget.CheckBox; import android.widget.EditText; import android.widget.Spinner; import android.widget.TextView; -import android.widget.ToggleButton; import java.security.cert.X509Certificate; @@ -45,15 +44,12 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On private static final int CHOOSE_FILE_OFFSET = 1000; private static final int UPDATE_ALIAS = 20; - private TextView mServerAddress; - private TextView mServerPort; private FileSelectLayout mClientCert; private FileSelectLayout mCaCert; private FileSelectLayout mClientKey; private TextView mAliasName; private TextView mAliasCertificate; private CheckBox mUseLzo; - private ToggleButton mTcpUdp; private Spinner mType; private FileSelectLayout mpkcs12; private TextView mPKCS12Password; @@ -129,14 +125,11 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On mView = inflater.inflate(R.layout.basic_settings,container,false); mProfileName = (EditText) mView.findViewById(R.id.profilename); - mServerAddress = (TextView) mView.findViewById(R.id.address); - mServerPort = (TextView) mView.findViewById(R.id.port); mClientCert = (FileSelectLayout) mView.findViewById(R.id.certselect); mClientKey = (FileSelectLayout) mView.findViewById(R.id.keyselect); mCaCert = (FileSelectLayout) mView.findViewById(R.id.caselect); mpkcs12 = (FileSelectLayout) mView.findViewById(R.id.pkcs12select); mUseLzo = (CheckBox) mView.findViewById(R.id.lzo); - mTcpUdp = (ToggleButton) mView.findViewById(id.tcpudp); mType = (Spinner) mView.findViewById(R.id.type); mPKCS12Password = (TextView) mView.findViewById(R.id.pkcs12password); mAliasName = (TextView) mView.findViewById(R.id.aliasname); @@ -262,9 +255,6 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On mCaCert.setData(mProfile.mCaFilename, getActivity()); mUseLzo.setChecked(mProfile.mUseLzo); - mServerPort.setText(mProfile.mServerPort); - mServerAddress.setText(mProfile.mServerName); - mTcpUdp.setChecked(mProfile.mUseUdp); mType.setSelection(mProfile.mAuthenticationType); mpkcs12.setData(mProfile.mPKCS12Filename, getActivity()); mPKCS12Password.setText(mProfile.mPKCS12Password); @@ -284,10 +274,6 @@ public class Settings_Basic extends Fragment implements View.OnClickListener, On mProfile.mClientKeyFilename = mClientKey.getData(); mProfile.mUseLzo = mUseLzo.isChecked(); - mProfile.mServerPort =mServerPort.getText().toString(); - mProfile.mServerName = mServerAddress.getText().toString(); - mProfile.mUseUdp = mTcpUdp.isChecked(); - mProfile.mAuthenticationType = mType.getSelectedItemPosition(); mProfile.mPKCS12Filename = mpkcs12.getData(); mProfile.mPKCS12Password = mPKCS12Password.getText().toString(); diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Connections.java b/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Connections.java new file mode 100644 index 00000000..435bdb26 --- /dev/null +++ b/main/src/main/java/de/blinkt/openvpn/fragments/Settings_Connections.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2012-2014 Arne Schwabe + * Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + */ + +package de.blinkt.openvpn.fragments; + +import android.app.Fragment; +import android.os.Build; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.CheckBox; +import android.widget.GridView; +import android.widget.ImageButton; +import android.widget.TextView; +import android.widget.Toast; + +import org.w3c.dom.Text; + +import de.blinkt.openvpn.R; +import de.blinkt.openvpn.VpnProfile; +import de.blinkt.openvpn.core.ProfileManager; + +public class Settings_Connections extends Fragment implements View.OnClickListener { + private VpnProfile mProfile; + private ConnectionsAdapter mConnectionsAdapter; + private TextView mWarning; + private CheckBox mUseRandomRemote; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + String profileUuid = getArguments().getString(getActivity().getPackageName() + ".profileUUID"); + mProfile= ProfileManager.get(getActivity(), profileUuid); + getActivity().setTitle(getString(R.string.edit_profile_title, mProfile.getName())); + + setHasOptionsMenu(true); + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + inflater.inflate(R.menu.connections, menu); + super.onCreateOptionsMenu(menu, inflater); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + View v = inflater.inflate(R.layout.connections, container, false); + + GridView gridview = (GridView) v.findViewById(R.id.gridview); + + gridview.setOnItemClickListener(new AdapterView.OnItemClickListener() { + public void onItemClick(AdapterView<?> parent, View v, int position, long id) { + Toast.makeText(getActivity(), "" + position, Toast.LENGTH_SHORT).show(); + } + }); + + mConnectionsAdapter = new ConnectionsAdapter(getActivity(), this, mProfile); + gridview.setAdapter(mConnectionsAdapter); + + ImageButton fab_button = (ImageButton) v.findViewById(R.id.add_new_remote); + if(fab_button!=null) + fab_button.setOnClickListener(this); + + mUseRandomRemote = (CheckBox) v.findViewById(R.id.remote_random); + mUseRandomRemote.setChecked(mProfile.mRemoteRandom); + + mWarning = (TextView) v.findViewById(R.id.noserver_active_warning); + return v; + } + + + + @Override + public void onClick(View v) { + if (v.getId() == R.id.add_new_remote) { + mConnectionsAdapter.addRemote(); + } + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId()==R.id.add_new_remote) + mConnectionsAdapter.addRemote(); + return super.onOptionsItemSelected(item); + } + + @Override + public void onPause() { + super.onPause(); + mConnectionsAdapter.saveProfile(); + mProfile.mRemoteRandom = mUseRandomRemote.isChecked(); + } + + public void setWarningVisible(int showWarning) { + mWarning.setVisibility(showWarning); + } +} diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/Utils.java b/main/src/main/java/de/blinkt/openvpn/fragments/Utils.java index 2408956c..8676ccaf 100644 --- a/main/src/main/java/de/blinkt/openvpn/fragments/Utils.java +++ b/main/src/main/java/de/blinkt/openvpn/fragments/Utils.java @@ -109,7 +109,6 @@ public class Utils { // DocumentsContract.EXTRA_SHOW_ADVANCED is hidden i.putExtra("android.content.extra.SHOW_ADVANCED", true); - /* Samsung has decided to do something strange, on stock Android GET_CONTENT opens the document UI */ /* fist try with documentsui */ i.setPackage("com.android.documentsui"); diff --git a/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java b/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java index e4911a41..d13d1ca6 100644 --- a/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java +++ b/main/src/main/java/de/blinkt/openvpn/fragments/VPNProfileList.java @@ -135,12 +135,8 @@ public class VPNProfileList extends ListFragment { TextView newvpntext = (TextView) v.findViewById(R.id.add_new_vpn_hint); TextView importvpntext = (TextView) v.findViewById(R.id.import_vpn_hint); - - newvpntext.setText(Html.fromHtml(getString(R.string.add_new_vpn_hint),new MiniImageGetter(),null)); importvpntext.setText(Html.fromHtml(getString(R.string.vpn_import_hint),new MiniImageGetter(),null)); - - return v; diff --git a/main/src/main/res/anim/fab_anim.xml b/main/src/main/res/anim/fab_anim.xml new file mode 100644 index 00000000..40d72b9a --- /dev/null +++ b/main/src/main/res/anim/fab_anim.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> +<item android:state_pressed="true"> + <objectAnimator + android:propertyName="translationZ" + android:duration="@android:integer/config_shortAnimTime" + android:valueFrom="@dimen/elevation_low" + android:valueTo="@dimen/elevation_high" + android:valueType="floatType"/> +</item> +<item> + <objectAnimator + android:propertyName="translationZ" + android:duration="@android:integer/config_shortAnimTime" + android:valueFrom="@dimen/elevation_high" + android:valueTo="@dimen/elevation_low" + android:valueType="floatType"/> +</item> +</selector> diff --git a/main/src/main/res/drawable/oval_ripple.xml b/main/src/main/res/drawable/oval_ripple.xml new file mode 100644 index 00000000..65f754d2 --- /dev/null +++ b/main/src/main/res/drawable/oval_ripple.xml @@ -0,0 +1,13 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<ripple xmlns:android="http://schemas.android.com/apk/res/android" + android:color="?android:colorControlHighlight"> + <item> + <shape android:shape="oval"> + <solid android:color="?android:colorAccent" /> + </shape> + </item> +</ripple> diff --git a/main/src/main/res/drawable/white_rect.xml b/main/src/main/res/drawable/white_rect.xml new file mode 100644 index 00000000..bb5a9fba --- /dev/null +++ b/main/src/main/res/drawable/white_rect.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<shape xmlns:android="http://schemas.android.com/apk/res/android"> + <solid android:color="#42000000" /> + <corners android:radius="5dp" /> + +</shape>
\ No newline at end of file diff --git a/main/src/main/res/layout-v21/connection_fab.xml b/main/src/main/res/layout-v21/connection_fab.xml new file mode 100644 index 00000000..571a2a17 --- /dev/null +++ b/main/src/main/res/layout-v21/connection_fab.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> + + +<!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<ImageButton xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/add_new_remote" + android:layout_width="@dimen/round_button_diameter" + android:layout_height="@dimen/round_button_diameter" + android:layout_alignParentBottom="true" + android:layout_alignParentRight="true" + android:layout_marginBottom="16dp" + android:layout_marginRight="16dp" + android:background="@drawable/oval_ripple" + android:elevation="1dp" + android:src="@android:drawable/ic_input_add" + android:stateListAnimator="@anim/fab_anim" + android:tint="@android:color/white" + tools:showIn="@layout/connections" />
\ No newline at end of file diff --git a/main/src/main/res/layout/basic_settings.xml b/main/src/main/res/layout/basic_settings.xml index 0a27a3e5..f8546a1b 100644 --- a/main/src/main/res/layout/basic_settings.xml +++ b/main/src/main/res/layout/basic_settings.xml @@ -27,52 +27,7 @@ style="@style/item" android:inputType="text" /> - <TextView - style="@style/item" - android:text="@string/address" - android:textAppearance="?android:attr/textAppearanceSmall" /> - - <EditText - android:id="@+id/address" - style="@style/item" - android:inputType="textUri" - android:text="@string/defaultserver" > - - <!-- <requestFocus /> --> - </EditText> - - <TextView - style="@style/item" - android:text="@string/port" - android:textAppearance="?android:attr/textAppearanceSmall" /> - - <RelativeLayout - android:id="@+id/relativeLayout1" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:layout_marginLeft="8dip" - android:layout_marginRight="8dip" > - <EditText - android:id="@+id/port" - style="@style/item" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentLeft="true" - android:layout_alignParentTop="true" - android:inputType="number" - android:text="@string/defaultport" /> - - <ToggleButton - android:id="@+id/tcpudp" - style="@style/accountSetupButton" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_alignParentRight="true" - android:layout_alignParentTop="true" - android:textOff="TCP" - android:textOn="UDP" /> - </RelativeLayout> <CheckBox android:id="@+id/lzo" diff --git a/main/src/main/res/layout/connection_fab.xml b/main/src/main/res/layout/connection_fab.xml new file mode 100644 index 00000000..0cf6ca95 --- /dev/null +++ b/main/src/main/res/layout/connection_fab.xml @@ -0,0 +1,11 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" android:layout_width="match_parent" + android:layout_height="match_parent"> + +</FrameLayout>
\ No newline at end of file diff --git a/main/src/main/res/layout/connections.xml b/main/src/main/res/layout/connections.xml new file mode 100644 index 00000000..30494bef --- /dev/null +++ b/main/src/main/res/layout/connections.xml @@ -0,0 +1,52 @@ +<?xml version="1.0" encoding="utf-8"?><!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:animateLayoutChanges="true" + android:orientation="vertical"> + + + + <TextView + android:id="@+id/noserver_active_warning" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_margin="10dp" + android:background="@drawable/white_rect" + android:drawableLeft="@drawable/ic_dialog_alert" + android:drawablePadding="10dp" + android:elevation="10dp" + android:gravity="center_vertical" + android:padding="@dimen/stdpadding" + android:text="You need to enable at least one server." + android:visibility="visible" + tools:visibility="visible" /> + + <CheckBox + android:text="Use connection entries in random order on connect" + android:id="@+id/remote_random" + android:layout_below="@id/noserver_active_warning" + android:padding="@dimen/stdpadding" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <GridView + android:layout_below="@id/remote_random" + android:id="@+id/gridview" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:columnWidth="290dp" + android:gravity="center" + android:horizontalSpacing="0dp" + android:numColumns="auto_fit" + android:stretchMode="columnWidth" + android:verticalSpacing="10dp" /> + + <include layout="@layout/connection_fab" /> + +</RelativeLayout>
\ No newline at end of file diff --git a/main/src/main/res/layout/server_card.xml b/main/src/main/res/layout/server_card.xml new file mode 100644 index 00000000..d9051b29 --- /dev/null +++ b/main/src/main/res/layout/server_card.xml @@ -0,0 +1,195 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<FrameLayout xmlns:tools="http://schemas.android.com/tools" + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:card_view="http://schemas.android.com/apk/res-auto" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="30dp" + > + <!-- A CardView that contains a TextView --> + <android.support.v7.widget.CardView + android:id="@+id/card_view" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:layout_gravity="center" + card_view:cardCornerRadius="10dp" + > + + + <RelativeLayout + android:animateLayoutChanges="true" + android:padding="5dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + xmlns:android="http://schemas.android.com/apk/res/android"> + + + <TextView + style="@style/item" + android:id="@+id/port_label" + android:text="@string/port" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:textAppearance="?android:attr/textAppearanceSmall" /> + + + <TextView + style="@style/item" + android:text="@string/address" + android:id="@+id/server_label" + android:textAppearance="?android:attr/textAppearanceSmall" /> + + + <EditText + android:id="@+id/portnumber" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:layout_below="@id/port_label" + android:layout_width="wrap_content" + + android:layout_height="wrap_content" + android:inputType="numberDecimal" + android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium" + android:text="1194" /> + + + <EditText + android:id="@+id/servername" + android:singleLine="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:inputType="textUri" + android:layout_below="@id/server_label" + android:layout_toLeftOf="@id/portnumber" + android:layout_toStartOf="@id/portnumber" + android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium" + android:text="openvpn.blinkt.de" /> + + <TextView + android:paddingTop="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/servername" + android:id="@+id/protocol" + android:text="Protocol" /> + + <RadioGroup + android:id="@+id/udptcpradiogroup" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/protocol" + android:orientation="horizontal" + android:paddingLeft="20dp"> + + <RadioButton + android:id="@+id/udp_proto" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="UDP" /> + + <Space + android:layout_width="20dp" + android:layout_height="wrap_content" /> + + <RadioButton + android:id="@+id/tcp_proto" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="TCP" /> + </RadioGroup> + + <!-- <TextView + android:paddingTop="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/udptcpradiogroup" + android:id="@+id/proxytext" + android:text="Proxy" /> + + <RadioGroup + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:id="@+id/proxygroup" + android:paddingLeft="20dp" + android:layout_below="@id/proxytext"> + + <RadioButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="None" /> + + <Space + android:layout_width="10dp" + android:layout_height="wrap_content" /> + + <RadioButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="HTTP" /> + + <Space + android:layout_width="10dp" + android:layout_height="wrap_content" /> + + <RadioButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Socks" /> + </RadioGroup> --> + + + <CheckBox + android:paddingTop="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/udptcpradiogroup" + android:id="@+id/use_customoptions" + android:text="Custom Options" /> + + <LinearLayout + android:paddingLeft="10dp" + android:paddingStart="10dp" + android:id="@+id/custom_options_layout" + android:layout_below="@id/use_customoptions" + android:layout_width="wrap_content" + android:orientation="vertical" + android:layout_height="wrap_content"> + <TextView + android:id="@+id/warnung_custom" + android:text="Specify custom connection specific options. Use with care" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <EditText + android:inputType="textMultiLine" + android:id="@+id/customoptions" + android:lines="5" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> + + <Switch + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Enable" + android:id="@+id/remoteSwitch" + android:layout_gravity="right|bottom" + android:layout_below="@+id/portnumber" + android:layout_alignRight="@+id/portnumber" + android:layout_alignEnd="@+id/portnumber" /> + + + </RelativeLayout> + </android.support.v7.widget.CardView> + +</FrameLayout>
\ No newline at end of file diff --git a/main/src/main/res/layout/server_layout b/main/src/main/res/layout/server_layout new file mode 100644 index 00000000..c6d11fe6 --- /dev/null +++ b/main/src/main/res/layout/server_layout @@ -0,0 +1,172 @@ +<!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<RelativeLayout + android:padding="5dp" + android:layout_width="match_parent" + android:layout_height="wrap_content" + xmlns:android="http://schemas.android.com/apk/res/android"> + + + <TextView + style="@style/item" + android:id="@+id/port_label" + android:text="@string/port" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:textAppearance="?android:attr/textAppearanceSmall" /> + + + <TextView + style="@style/item" + android:text="@string/address" + android:id="@+id/server_label" + android:textAppearance="?android:attr/textAppearanceSmall" /> + + + <EditText + android:id="@+id/portnumber" + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + android:layout_below="@id/port_label" + android:layout_width="wrap_content" + + android:layout_height="wrap_content" + android:inputType="numberDecimal" + android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium" + android:text="1194" /> + + + <EditText + android:id="@+id/servername" + android:singleLine="true" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:inputType="textUri" + android:layout_below="@id/server_label" + android:layout_toLeftOf="@id/portnumber" + android:layout_toStartOf="@id/portnumber" + android:textAppearance="@android:style/TextAppearance.DeviceDefault.Medium" + android:text="openvpn.blinkt.de" /> + + <TextView + android:paddingTop="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/servername" + android:id="@+id/protocol" + android:text="Protocol" /> + + <RadioGroup + android:id="@+id/udptcpradiogroup" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/protocol" + android:orientation="horizontal" + android:paddingLeft="20dp"> + + <RadioButton + android:id="@+id/udp_proto" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="UDP" /> + + <Space + android:layout_width="20dp" + android:layout_height="wrap_content" /> + + <RadioButton + android:id="@+id/tcp_proto" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="TCP" /> + </RadioGroup> + +<!-- <TextView + android:paddingTop="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/udptcpradiogroup" + android:id="@+id/proxytext" + android:text="Proxy" /> + + <RadioGroup + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:id="@+id/proxygroup" + android:paddingLeft="20dp" + android:layout_below="@id/proxytext"> + + <RadioButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="None" /> + + <Space + android:layout_width="10dp" + android:layout_height="wrap_content" /> + + <RadioButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="HTTP" /> + + <Space + android:layout_width="10dp" + android:layout_height="wrap_content" /> + + <RadioButton + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Socks" /> + </RadioGroup> --> + + + <CheckBox + android:paddingTop="10dp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/udptcpradiogroup" + android:id="@+id/use_customoptions" + android:text="Custom Options" /> + + <LinearLayout + android:paddingLeft="10dp" + android:paddingStart="10dp" + android:layout_below="@id/use_customoptions" + android:layout_width="wrap_content" + android:orientation="vertical" + android:layout_height="wrap_content"> + <TextView + android:id="@+id/warnung_custom" + android:text="Specify custom connection specific options. Use with care" + android:layout_width="wrap_content" + android:layout_height="wrap_content" /> + + <EditText + android:inputType="textMultiLine" + android:id="@+id/customoptions" + android:lines="5" + android:layout_width="match_parent" + android:layout_height="wrap_content" /> + </LinearLayout> + + <Switch + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="Enable" + android:id="@+id/remoteSwitch" + android:layout_gravity="right|bottom" + android:layout_below="@+id/portnumber" + android:layout_alignRight="@+id/portnumber" + android:layout_alignEnd="@+id/portnumber" /> + + +</RelativeLayout>
\ No newline at end of file diff --git a/main/src/main/res/layout/vpn_profile_list.xml b/main/src/main/res/layout/vpn_profile_list.xml index 18184e5d..79272f6c 100644 --- a/main/src/main/res/layout/vpn_profile_list.xml +++ b/main/src/main/res/layout/vpn_profile_list.xml @@ -1,71 +1,89 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- +<?xml version="1.0" encoding="utf-8"?><!-- ~ Copyright (c) 2012-2014 Arne Schwabe ~ Distributed under the GNU GPL v2 with additional terms. For full terms see the file doc/LICENSE.txt --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:paddingLeft="@dimen/stdpadding" - android:paddingRight="@dimen/stdpadding" > - - <ListView - android:descendantFocusability="afterDescendants" - android:id="@android:id/list" - android:layout_width="fill_parent" - android:layout_height="fill_parent" /> + android:layout_height="match_parent"> <LinearLayout - android:id="@android:id/empty" + android:layout_width="match_parent" android:layout_height="match_parent" - android:gravity="center" - android:orientation="vertical" > + android:orientation="vertical" + android:paddingLeft="@dimen/stdpadding" + android:paddingRight="@dimen/stdpadding"> - <TextView + <ListView + android:id="@android:id/list" android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="@string/no_vpn_profiles_defined" - android:textAppearance="?android:attr/textAppearanceLarge" /> + android:layout_height="fill_parent" + android:descendantFocusability="afterDescendants" /> - <Space + <LinearLayout + android:id="@android:id/empty" android:layout_width="match_parent" - android:layout_height="12sp" /> + android:layout_height="match_parent" + android:gravity="center" + android:orientation="vertical"> + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/no_vpn_profiles_defined" + android:textAppearance="?android:attr/textAppearanceLarge" /> - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="@string/openvpn_is_no_free_vpn" - android:autoLink="web" /> - <Space - android:layout_width="match_parent" - android:layout_height="12sp" /> + <Space + android:layout_width="match_parent" + android:layout_height="12sp" /> - <TextView - android:id="@+id/add_new_vpn_hint" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - tools:ignore="SelectableText" /> + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:autoLink="web" + android:text="@string/openvpn_is_no_free_vpn" /> - <TextView - android:id="@+id/import_vpn_hint" - android:layout_width="fill_parent" - android:layout_height="wrap_content" - tools:ignore="SelectableText" /> + <Space + android:layout_width="match_parent" + android:layout_height="12sp" /> - <Space - android:layout_width="match_parent" - android:layout_height="12sp" /> - <TextView - android:layout_width="fill_parent" - android:layout_height="wrap_content" - android:text="@string/faq_hint" /> + <TextView + android:id="@+id/add_new_vpn_hint" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + tools:ignore="SelectableText" /> + + <TextView + android:id="@+id/import_vpn_hint" + android:layout_width="fill_parent" + android:layout_height="wrap_content" + tools:ignore="SelectableText" /> + + <Space + android:layout_width="match_parent" + android:layout_height="12sp" /> + + <TextView + android:layout_width="fill_parent" + android:layout_height="wrap_content" + android:text="@string/faq_hint" /> + </LinearLayout> + </LinearLayout> -</LinearLayout>
\ No newline at end of file +<!-- <ImageButton + android:id="@+id/add_button" + android:layout_width="@dimen/round_button_diameter" + android:layout_height="@dimen/round_button_diameter" + android:layout_gravity="end|bottom" + android:tint="@android:color/white" + android:background="@drawable/oval_ripple" + android:elevation="@dimen/elevation_low" + android:visibility="gone" + android:layout_marginBottom="@dimen/add_button_margin" + android:layout_marginEnd="@dimen/add_button_margin" + android:src="@drawable/ic_archive_grey600_24dp" /> --> +</FrameLayout> diff --git a/main/src/main/res/menu/connections.xml b/main/src/main/res/menu/connections.xml new file mode 100644 index 00000000..7446746f --- /dev/null +++ b/main/src/main/res/menu/connections.xml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (c) 2012-2014 Arne Schwabe + ~ Distributed under the GNU GPL v2. For full terms see the file doc/LICENSE.txt + --> + +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + <item + android:icon="@drawable/ic_menu_add" + android:title="Add new remote" + android:id="@+id/add_new_remote" + android:titleCondensed="@string/add" + android:showAsAction="ifRoom|withText" + /> +</menu>
\ No newline at end of file diff --git a/main/src/main/res/values/dimens.xml b/main/src/main/res/values/dimens.xml index 9b43bd12..d46cfa98 100644 --- a/main/src/main/res/values/dimens.xml +++ b/main/src/main/res/values/dimens.xml @@ -9,4 +9,11 @@ <dimen name="stdpadding">8dp</dimen> <bool name="logSildersAlwaysVisible">false</bool> + <dimen name="diameter">48dp</dimen> + <dimen name="elevation_low">1dp</dimen> + <dimen name="elevation_high">4dp</dimen> + <dimen name="add_button_margin">16dp</dimen> + <dimen name="round_button_diameter">56dp</dimen> + + </resources>
\ No newline at end of file diff --git a/main/src/main/res/xml/vpn_headers.xml b/main/src/main/res/xml/vpn_headers.xml index 65f5e825..576aea8b 100644 --- a/main/src/main/res/xml/vpn_headers.xml +++ b/main/src/main/res/xml/vpn_headers.xml @@ -6,11 +6,12 @@ <preference-headers xmlns:android="http://schemas.android.com/apk/res/android" > - <header android:fragment="de.blinkt.openvpn.fragments.Settings_Behaviour" android:title="@string/client_behaviour" /> + <header android:fragment="de.blinkt.openvpn.fragments.Settings_Connections" + android:title="Server List" /> <header android:id="@+id/basicsettingsid" android:fragment="de.blinkt.openvpn.fragments.Settings_Basic" |