Merge "Support VPN Proxy(2/2)"
diff --git a/res/layout/vpn_dialog.xml b/res/layout/vpn_dialog.xml
index 71ce0ad..a626205 100644
--- a/res/layout/vpn_dialog.xml
+++ b/res/layout/vpn_dialog.xml
@@ -140,6 +140,46 @@
                     android:labelFor="@+id/routes"/>
             <EditText style="@style/vpn_value" android:id="@+id/routes"
                     android:hint="@string/vpn_not_used"/>
+
+            <TextView android:id="@+id/vpn_proxy_settings_title"
+                      style="@style/vpn_label"
+                      android:text="@string/proxy_settings_title"
+                      android:labelFor="@+id/vpn_proxy_settings" />
+
+            <Spinner android:id="@+id/vpn_proxy_settings"
+                     style="@style/vpn_value"
+                     android:prompt="@string/proxy_settings_title"
+                     android:entries="@array/vpn_proxy_settings" />
+
+            <LinearLayout
+                android:id="@+id/vpn_proxy_fields"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:visibility="gone" >
+
+                <TextView
+                    style="@style/vpn_label"
+                    android:text="@string/proxy_hostname_label"
+                    android:labelFor="@+id/vpn_proxy_host" />
+
+                <EditText
+                    android:id="@+id/vpn_proxy_host"
+                    style="@style/vpn_value"
+                    android:hint="@string/proxy_hostname_hint"
+                    android:inputType="textNoSuggestions" />
+
+                <TextView
+                    style="@style/vpn_label"
+                    android:text="@string/proxy_port_label"
+                    android:labelFor="@+id/vpn_proxy_port" />
+
+                <EditText
+                    android:id="@+id/vpn_proxy_port"
+                    style="@style/vpn_value"
+                    android:hint="@string/proxy_port_hint"
+                    android:inputType="number" />
+            </LinearLayout>
         </LinearLayout>
 
         <LinearLayout android:id="@+id/login"
diff --git a/res/values/arrays.xml b/res/values/arrays.xml
index be7a0ca..a3a9865 100644
--- a/res/values/arrays.xml
+++ b/res/values/arrays.xml
@@ -939,6 +939,14 @@
         <item>IPSec VPN with certificates and hybrid authentication</item>
     </string-array>
 
+    <!-- VPN proxy settings. -->
+    <string-array name="vpn_proxy_settings">
+        <!-- No HTTP proxy is used for the current VPN [CHAR LIMIT=25] -->
+        <item>None</item>
+        <!-- Manual HTTP proxy settings are used for the current VPN [CHAR LIMIT=25] -->
+        <item>Manual</item>
+    </string-array>
+
     <!-- Match this with the constants in LegacyVpnInfo. --> <skip />
     <!-- Status for a VPN network. [CHAR LIMIT=100] -->
     <string-array name="vpn_states">
diff --git a/src/com/android/settings/vpn2/ConfigDialog.java b/src/com/android/settings/vpn2/ConfigDialog.java
index 4eecd28..0c6d061 100644
--- a/src/com/android/settings/vpn2/ConfigDialog.java
+++ b/src/com/android/settings/vpn2/ConfigDialog.java
@@ -19,6 +19,8 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.net.Proxy;
+import android.net.ProxyInfo;
 import android.os.Bundle;
 import android.os.SystemProperties;
 import android.security.Credentials;
@@ -66,6 +68,9 @@
     private TextView mSearchDomains;
     private TextView mDnsServers;
     private TextView mRoutes;
+    private Spinner mProxySettings;
+    private TextView mProxyHost;
+    private TextView mProxyPort;
     private CheckBox mMppe;
     private TextView mL2tpSecret;
     private TextView mIpsecIdentifier;
@@ -104,6 +109,9 @@
         mSearchDomains = (TextView) mView.findViewById(R.id.search_domains);
         mDnsServers = (TextView) mView.findViewById(R.id.dns_servers);
         mRoutes = (TextView) mView.findViewById(R.id.routes);
+        mProxySettings = (Spinner) mView.findViewById(R.id.vpn_proxy_settings);
+        mProxyHost = (TextView) mView.findViewById(R.id.vpn_proxy_host);
+        mProxyPort = (TextView) mView.findViewById(R.id.vpn_proxy_port);
         mMppe = (CheckBox) mView.findViewById(R.id.mppe);
         mL2tpSecret = (TextView) mView.findViewById(R.id.l2tp_secret);
         mIpsecIdentifier = (TextView) mView.findViewById(R.id.ipsec_identifier);
@@ -127,6 +135,11 @@
         mSearchDomains.setText(mProfile.searchDomains);
         mDnsServers.setText(mProfile.dnsServers);
         mRoutes.setText(mProfile.routes);
+        if (mProfile.proxy != null) {
+            mProxyHost.setText(mProfile.proxy.getHost());
+            int port = mProfile.proxy.getPort();
+            mProxyPort.setText(port == 0 ? "" : Integer.toString(port));
+        }
         mMppe.setChecked(mProfile.mppe);
         mL2tpSecret.setText(mProfile.l2tpSecret);
         mIpsecIdentifier.setText(mProfile.ipsecIdentifier);
@@ -152,6 +165,9 @@
         mPassword.addTextChangedListener(this);
         mDnsServers.addTextChangedListener(this);
         mRoutes.addTextChangedListener(this);
+        mProxySettings.setOnItemSelectedListener(this);
+        mProxyHost.addTextChangedListener(this);
+        mProxyPort.addTextChangedListener(this);
         mIpsecSecret.addTextChangedListener(this);
         mIpsecUserCert.setOnItemSelectedListener(this);
         mShowOptions.setOnClickListener(this);
@@ -174,7 +190,8 @@
 
             // Switch to advanced view immediately if any advanced options are on
             if (!mProfile.searchDomains.isEmpty() || !mProfile.dnsServers.isEmpty() ||
-                    !mProfile.routes.isEmpty()) {
+                    !mProfile.routes.isEmpty() || (mProfile.proxy != null &&
+                    (!mProfile.proxy.getHost().isEmpty() || mProfile.proxy.getPort() != 0))) {
                 showAdvancedOptions();
             }
 
@@ -245,6 +262,8 @@
     public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
         if (parent == mType) {
             changeType(position);
+        } else if (parent == mProxySettings) {
+            updateProxyFieldsVisibility(position);
         }
         updateUiControls();
     }
@@ -270,6 +289,7 @@
      * These include:
      * "Always-on VPN" checkbox
      * Reason for "Always-on VPN" being disabled, when necessary
+     * Proxy info if manually configured
      * "Save account information" checkbox
      * "Save" and "Connect" buttons
      */
@@ -297,6 +317,13 @@
             mAlwaysOnInvalidReason.setVisibility(View.VISIBLE);
         }
 
+        // Show proxy fields if any proxy field is filled.
+        if (mProfile.proxy != null && (!mProfile.proxy.getHost().isEmpty() ||
+                mProfile.proxy.getPort() != 0)) {
+            mProxySettings.setSelection(VpnProfile.PROXY_MANUAL);
+            updateProxyFieldsVisibility(VpnProfile.PROXY_MANUAL);
+        }
+
         // Save account information
         if (mAlwaysOnVpn.isChecked()) {
             mSaveLogin.setChecked(true);
@@ -310,6 +337,11 @@
         getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validate(mEditing));
     }
 
+    private void updateProxyFieldsVisibility(int position) {
+        final int visible = position == VpnProfile.PROXY_MANUAL ? View.VISIBLE : View.GONE;
+        mView.findViewById(R.id.vpn_proxy_fields).setVisibility(visible);
+    }
+
     private void showAdvancedOptions() {
         mView.findViewById(R.id.options).setVisibility(View.VISIBLE);
         mShowOptions.setVisibility(View.GONE);
@@ -360,6 +392,11 @@
                 !validateAddresses(mRoutes.getText().toString(), true)) {
             return false;
         }
+
+        if (!validateProxy()) {
+            return false;
+        }
+
         switch (mType.getSelectedItemPosition()) {
             case VpnProfile.TYPE_PPTP:
             case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
@@ -434,6 +471,10 @@
         return mEditing;
     }
 
+    boolean hasProxy() {
+        return mProxySettings.getSelectedItemPosition() == VpnProfile.PROXY_MANUAL;
+    }
+
     VpnProfile getProfile() {
         // First, save common fields.
         VpnProfile profile = new VpnProfile(mProfile.key);
@@ -445,7 +486,16 @@
         profile.searchDomains = mSearchDomains.getText().toString().trim();
         profile.dnsServers = mDnsServers.getText().toString().trim();
         profile.routes = mRoutes.getText().toString().trim();
-
+        if (hasProxy()) {
+            String proxyHost = mProxyHost.getText().toString().trim();
+            String proxyPort = mProxyPort.getText().toString().trim();
+            // 0 is a last resort default, but the interface validates that the proxy port is
+            // present and non-zero.
+            int port = proxyPort.isEmpty() ? 0 : Integer.parseInt(proxyPort);
+            profile.proxy = new ProxyInfo(proxyHost, port, null);
+        } else {
+            profile.proxy = null;
+        }
         // Then, save type-specific fields.
         switch (profile.type) {
             case VpnProfile.TYPE_PPTP:
@@ -482,4 +532,15 @@
         profile.saveLogin = mSaveLogin.isChecked() || (mEditing && hasLogin);
         return profile;
     }
+
+    private boolean validateProxy() {
+        if (!hasProxy()) {
+            return true;
+        }
+
+        final String host = mProxyHost.getText().toString().trim();
+        final String port = mProxyPort.getText().toString().trim();
+        return Proxy.validate(host, port, "") == Proxy.PROXY_VALID;
+    }
+
 }