Settings: revise VpnSettings.
Make the style closer to other settings. Profiles are saved in KeyStore with optional account information. Not adapt to IConnectivityManager yet. Change-Id: I9d7a0c14b253a0b355499c5e558b0761fa24ea22
This commit is contained in:
@@ -612,5 +612,39 @@
|
|||||||
<item>Use HDCP checking for DRM content only</item>
|
<item>Use HDCP checking for DRM content only</item>
|
||||||
<item>Always use HDCP checking</item>
|
<item>Always use HDCP checking</item>
|
||||||
</string-array>
|
</string-array>
|
||||||
</resources>
|
|
||||||
|
|
||||||
|
<!-- Match this with the constants in VpnProfile. --> <skip />
|
||||||
|
<!-- Short names for each VPN type, not really translatable. [CHAR LIMIT=20] -->
|
||||||
|
<string-array name="vpn_types" translatable="false">
|
||||||
|
<item>PPTP</item>
|
||||||
|
<item>L2TP/IPSec PSK</item>
|
||||||
|
<item>L2TP/IPSec RSA</item>
|
||||||
|
<item>IPSec Xauth PSK</item>
|
||||||
|
<item>IPSec Xauth RSA</item>
|
||||||
|
<item>IPSec Hybrid RSA</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<!-- Match this with the constants in VpnProfile. --> <skip />
|
||||||
|
<!-- Longer descriptions for each VPN type. [CHAR LIMIT=100] -->
|
||||||
|
<string-array name="vpn_types_long">
|
||||||
|
<item>PPTP VPN</item>
|
||||||
|
<item>L2TP/IPSec VPN with pre-shared keys</item>
|
||||||
|
<item>L2TP/IPSec VPN with certificates</item>
|
||||||
|
<item>IPSec VPN with pre-shared keys and Xauth authentication</item>
|
||||||
|
<item>IPSec VPN with certificates and Xauth authentication</item>
|
||||||
|
<item>IPSec VPN with certificates and hybrid authentication</item>
|
||||||
|
</string-array>
|
||||||
|
|
||||||
|
<!-- Match this with the constants in VpnProfile. --> <skip />
|
||||||
|
<!-- Status for a VPN network. [CHAR LIMIT=100] -->
|
||||||
|
<string-array name="vpn_states">
|
||||||
|
<!-- Status message when VPN is connecting. -->
|
||||||
|
<item>Connecting\u2026</item>
|
||||||
|
<!-- Status message when VPN is connected. -->
|
||||||
|
<item>Connected</item>
|
||||||
|
<!-- Status message when VPN is disconnected. -->
|
||||||
|
<item>Disconnected</item>
|
||||||
|
<!-- Status message when VPN failed to connect. -->
|
||||||
|
<item>Failed</item>
|
||||||
|
</string-array>
|
||||||
|
</resources>
|
||||||
|
@@ -2912,8 +2912,6 @@ found in the list of installed applications.</string>
|
|||||||
|
|
||||||
<string name="vpn_settings_activity_title">VPN settings</string>
|
<string name="vpn_settings_activity_title">VPN settings</string>
|
||||||
|
|
||||||
<!-- Title of VPN connect dialog -->
|
|
||||||
<string name="vpn_connect_to">Connect to <xliff:g id="name" example="Work Network">%s</xliff:g></string>
|
|
||||||
<!-- In VPN connect dialog, for inputing username and password -->
|
<!-- In VPN connect dialog, for inputing username and password -->
|
||||||
<string name="vpn_username_colon">Username:</string>
|
<string name="vpn_username_colon">Username:</string>
|
||||||
<string name="vpn_password_colon">Password:</string>
|
<string name="vpn_password_colon">Password:</string>
|
||||||
@@ -2934,8 +2932,6 @@ found in the list of installed applications.</string>
|
|||||||
<string name="vpn_menu_revert">Revert</string>
|
<string name="vpn_menu_revert">Revert</string>
|
||||||
<string name="vpn_menu_connect">Connect to network</string>
|
<string name="vpn_menu_connect">Connect to network</string>
|
||||||
<string name="vpn_menu_disconnect">Disconnect from network</string>
|
<string name="vpn_menu_disconnect">Disconnect from network</string>
|
||||||
<string name="vpn_menu_edit">Edit network</string>
|
|
||||||
<string name="vpn_menu_delete">Delete network</string>
|
|
||||||
|
|
||||||
<!-- VPN error dialog messages -->
|
<!-- VPN error dialog messages -->
|
||||||
<string name="vpn_error_miss_entering">You must enter <xliff:g id="code">%s</xliff:g>.</string>
|
<string name="vpn_error_miss_entering">You must enter <xliff:g id="code">%s</xliff:g>.</string>
|
||||||
@@ -2976,7 +2972,6 @@ found in the list of installed applications.</string>
|
|||||||
<string name="vpn_connect_hint">Connect to network</string>
|
<string name="vpn_connect_hint">Connect to network</string>
|
||||||
|
|
||||||
<!-- Name of a VPN profile -->
|
<!-- Name of a VPN profile -->
|
||||||
<string name="vpn_name">VPN name</string>
|
|
||||||
<string name="vpn_a_name">a VPN name</string>
|
<string name="vpn_a_name">a VPN name</string>
|
||||||
|
|
||||||
<!-- Toast message shown when a profile is added -->
|
<!-- Toast message shown when a profile is added -->
|
||||||
@@ -2998,7 +2993,6 @@ found in the list of installed applications.</string>
|
|||||||
<!-- Preference title -->
|
<!-- Preference title -->
|
||||||
<string name="vpn_l2tp_secret_string_title">Set L2TP secret</string>
|
<string name="vpn_l2tp_secret_string_title">Set L2TP secret</string>
|
||||||
<!-- Complete term -->
|
<!-- Complete term -->
|
||||||
<string name="vpn_l2tp_secret">L2TP secret</string>
|
|
||||||
<string name="vpn_a_l2tp_secret">an L2TP secret</string>
|
<string name="vpn_a_l2tp_secret">an L2TP secret</string>
|
||||||
<string name="vpn_pptp_encryption_title">encryption</string>
|
<string name="vpn_pptp_encryption_title">encryption</string>
|
||||||
<string name="vpn_pptp_encryption">PPTP encryption</string>
|
<string name="vpn_pptp_encryption">PPTP encryption</string>
|
||||||
@@ -3443,4 +3437,57 @@ found in the list of installed applications.</string>
|
|||||||
<!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] -->
|
<!-- Dialog button indicating that data connection should be re-enabled. [CHAR LIMIT=28] -->
|
||||||
<string name="data_usage_disabled_dialog_enable">re-enable data</string>
|
<string name="data_usage_disabled_dialog_enable">re-enable data</string>
|
||||||
|
|
||||||
|
<!-- Input label for the name of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_name">Name</string>
|
||||||
|
<!-- Input label for the type of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_type">Type</string>
|
||||||
|
<!-- Input label for the server address of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_server">Server address</string>
|
||||||
|
<!-- Checkbox label to enable PPP encryption for a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_mppe">PPP encryption (MPPE)</string>
|
||||||
|
<!-- Input label for the L2TP secret of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_l2tp_secret">L2TP secret</string>
|
||||||
|
<!-- Input label for the IPSec identifier of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_ipsec_identifier">IPSec identifier</string>
|
||||||
|
<!-- Input label for the IPSec pre-shared key of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_ipsec_secret">IPSec pre-shared key</string>
|
||||||
|
<!-- Selection label for the IPSec user certificate of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_ipsec_user_cert">IPSec user certificate</string>
|
||||||
|
<!-- Selection label for the IPSec CA certificate of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_ipsec_ca_cert">IPSec CA certificate</string>
|
||||||
|
<!-- Input label for the DNS search domains of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_domains">DNS search domains</string>
|
||||||
|
<!-- Input label for the forwarding routes of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_routes">Forwarding routes</string>
|
||||||
|
<!-- Input label for the username of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_username">Username</string>
|
||||||
|
<!-- Input label for the password of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_password">Password</string>
|
||||||
|
<!-- Checkbox label to save the username and the password for a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_save_login">Save this information</string>
|
||||||
|
|
||||||
|
<!-- Hint for an optional input of a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_not_used">(not used)</string>
|
||||||
|
<!-- Option to not use a CA certificate to verify the VPN server. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_no_ca_cert">(do not verify server)</string>
|
||||||
|
|
||||||
|
<!-- Button label to cancel chaning a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_cancel">Cancel</string>
|
||||||
|
<!-- Button label to save a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_save">Save</string>
|
||||||
|
<!-- Button label to connect to a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_connect">Connect</string>
|
||||||
|
<!-- Dialog title to edit a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_edit">Edit VPN network</string>
|
||||||
|
<!-- Dialog title to connect to a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_connect_to">Connect to <xliff:g id="network" example="School">%s</xliff:g></string>
|
||||||
|
|
||||||
|
<!-- Preference title for VPN settings. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_title">VPN settings</string>
|
||||||
|
<!-- Preference title to create a new VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_create">Add VPN network</string>
|
||||||
|
<!-- Menu item to edit a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_menu_edit">Edit network</string>
|
||||||
|
<!-- Menu item to delete a VPN network. [CHAR LIMIT=40] -->
|
||||||
|
<string name="vpn_menu_delete">Delete network</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
@@ -138,4 +138,14 @@
|
|||||||
<item name="android:singleLine">true</item>
|
<item name="android:singleLine">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="vpn_label">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:textSize">14sp</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
<style name="vpn_value">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
325
src/com/android/settings/vpn2/VpnDialog.java
Normal file
325
src/com/android/settings/vpn2/VpnDialog.java
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.vpn2;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import android.app.AlertDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.security.Credentials;
|
||||||
|
import android.security.KeyStore;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView;
|
||||||
|
import android.widget.AdapterView.OnItemSelectedListener;
|
||||||
|
import android.widget.ArrayAdapter;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.Spinner;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
class VpnDialog extends AlertDialog implements TextWatcher, OnItemSelectedListener {
|
||||||
|
private static final String DUMMY = "\r\r\r\r";
|
||||||
|
|
||||||
|
private static String getDummy(String secret) {
|
||||||
|
return secret.isEmpty() ? "" : DUMMY;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getSecret(TextView dummy) {
|
||||||
|
String secret = dummy.getText().toString();
|
||||||
|
return DUMMY.equals(secret) ? "" : secret;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final KeyStore mKeyStore = KeyStore.getInstance();
|
||||||
|
private final DialogInterface.OnClickListener mListener;
|
||||||
|
private final VpnProfile mProfile;
|
||||||
|
|
||||||
|
private boolean mEditing;
|
||||||
|
|
||||||
|
private View mView;
|
||||||
|
|
||||||
|
private TextView mName;
|
||||||
|
private Spinner mType;
|
||||||
|
private TextView mServer;
|
||||||
|
private TextView mUsername;
|
||||||
|
private TextView mPassword;
|
||||||
|
private TextView mDomains;
|
||||||
|
private TextView mRoutes;
|
||||||
|
private CheckBox mMppe;
|
||||||
|
private TextView mL2tpSecret;
|
||||||
|
private TextView mIpsecIdentifier;
|
||||||
|
private TextView mIpsecSecret;
|
||||||
|
private Spinner mIpsecUserCert;
|
||||||
|
private Spinner mIpsecCaCert;
|
||||||
|
private CheckBox mSaveLogin;
|
||||||
|
|
||||||
|
VpnDialog(Context context, DialogInterface.OnClickListener listener,
|
||||||
|
VpnProfile profile, boolean editing) {
|
||||||
|
super(context);
|
||||||
|
mListener = listener;
|
||||||
|
mProfile = profile;
|
||||||
|
mEditing = editing;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedState) {
|
||||||
|
mView = getLayoutInflater().inflate(R.layout.vpn_dialog, null);
|
||||||
|
setView(mView);
|
||||||
|
setInverseBackgroundForced(true);
|
||||||
|
|
||||||
|
Context context = getContext();
|
||||||
|
|
||||||
|
// First, find out all the fields.
|
||||||
|
mName = (TextView) mView.findViewById(R.id.name);
|
||||||
|
mType = (Spinner) mView.findViewById(R.id.type);
|
||||||
|
mServer = (TextView) mView.findViewById(R.id.server);
|
||||||
|
mUsername = (TextView) mView.findViewById(R.id.username);
|
||||||
|
mPassword = (TextView) mView.findViewById(R.id.password);
|
||||||
|
mDomains = (TextView) mView.findViewById(R.id.domains);
|
||||||
|
mRoutes = (TextView) mView.findViewById(R.id.routes);
|
||||||
|
mMppe = (CheckBox) mView.findViewById(R.id.mppe);
|
||||||
|
mL2tpSecret = (TextView) mView.findViewById(R.id.l2tp_secret);
|
||||||
|
mIpsecIdentifier = (TextView) mView.findViewById(R.id.ipsec_identifier);
|
||||||
|
mIpsecSecret = (TextView) mView.findViewById(R.id.ipsec_secret);
|
||||||
|
mIpsecUserCert = (Spinner) mView.findViewById(R.id.ipsec_user_cert);
|
||||||
|
mIpsecCaCert = (Spinner) mView.findViewById(R.id.ipsec_ca_cert);
|
||||||
|
mSaveLogin = (CheckBox) mView.findViewById(R.id.save_login);
|
||||||
|
|
||||||
|
// Second, copy values from the profile.
|
||||||
|
mName.setText(mProfile.name);
|
||||||
|
mType.setSelection(mProfile.type);
|
||||||
|
mServer.setText(mProfile.server);
|
||||||
|
mUsername.setText(mProfile.username);
|
||||||
|
mPassword.setText(getDummy(mProfile.password));
|
||||||
|
mDomains.setText(mProfile.domains);
|
||||||
|
mRoutes.setText(mProfile.routes);
|
||||||
|
mMppe.setChecked(mProfile.mppe);
|
||||||
|
mL2tpSecret.setText(getDummy(mProfile.l2tpSecret));
|
||||||
|
mIpsecIdentifier.setText(mProfile.ipsecIdentifier);
|
||||||
|
mIpsecSecret.setText(getDummy(mProfile.ipsecSecret));
|
||||||
|
loadCertificates(mIpsecUserCert, Credentials.USER_CERTIFICATE,
|
||||||
|
0, mProfile.ipsecUserCert);
|
||||||
|
loadCertificates(mIpsecUserCert, Credentials.CA_CERTIFICATE,
|
||||||
|
R.string.vpn_no_ca_cert, mProfile.ipsecCaCert);
|
||||||
|
mSaveLogin.setChecked(mProfile.saveLogin);
|
||||||
|
|
||||||
|
// Third, add listeners to required fields.
|
||||||
|
mName.addTextChangedListener(this);
|
||||||
|
mType.setOnItemSelectedListener(this);
|
||||||
|
mServer.addTextChangedListener(this);
|
||||||
|
mUsername.addTextChangedListener(this);
|
||||||
|
mPassword.addTextChangedListener(this);
|
||||||
|
mIpsecSecret.addTextChangedListener(this);
|
||||||
|
mIpsecUserCert.setOnItemSelectedListener(this);
|
||||||
|
|
||||||
|
// Forth, determine to do editing or connecting.
|
||||||
|
boolean valid = validate(true);
|
||||||
|
mEditing = mEditing || !valid;
|
||||||
|
|
||||||
|
if (mEditing) {
|
||||||
|
setTitle(R.string.vpn_edit);
|
||||||
|
|
||||||
|
// Show common fields.
|
||||||
|
mView.findViewById(R.id.editor).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// Show type-specific fields.
|
||||||
|
changeType(mProfile.type);
|
||||||
|
|
||||||
|
// Create a button to save the profile.
|
||||||
|
setButton(DialogInterface.BUTTON_POSITIVE,
|
||||||
|
context.getString(R.string.vpn_save), mListener);
|
||||||
|
} else {
|
||||||
|
setTitle(context.getString(R.string.vpn_connect_to, mProfile.name));
|
||||||
|
|
||||||
|
// Not editing, just show username and password.
|
||||||
|
mView.findViewById(R.id.login).setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
// Create a button to connect the network.
|
||||||
|
setButton(DialogInterface.BUTTON_POSITIVE,
|
||||||
|
context.getString(R.string.vpn_connect), mListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Always provide a cancel button.
|
||||||
|
setButton(DialogInterface.BUTTON_NEGATIVE,
|
||||||
|
context.getString(R.string.vpn_cancel), mListener);
|
||||||
|
|
||||||
|
// Let AlertDialog create everything.
|
||||||
|
super.onCreate(null);
|
||||||
|
|
||||||
|
// Disable the action button if necessary.
|
||||||
|
getButton(DialogInterface.BUTTON_POSITIVE)
|
||||||
|
.setEnabled(mEditing ? valid : validate(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable field) {
|
||||||
|
getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validate(mEditing));
|
||||||
|
}
|
||||||
|
|
||||||
|
@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 onItemSelected(AdapterView<?> parent, View view, int position, long id) {
|
||||||
|
if (parent == mType) {
|
||||||
|
changeType(position);
|
||||||
|
}
|
||||||
|
getButton(DialogInterface.BUTTON_POSITIVE).setEnabled(validate(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onNothingSelected(AdapterView<?> parent) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void changeType(int type) {
|
||||||
|
// First, hide everything.
|
||||||
|
mMppe.setVisibility(View.GONE);
|
||||||
|
mView.findViewById(R.id.l2tp).setVisibility(View.GONE);
|
||||||
|
mView.findViewById(R.id.ipsec_psk).setVisibility(View.GONE);
|
||||||
|
mView.findViewById(R.id.ipsec_user).setVisibility(View.GONE);
|
||||||
|
mView.findViewById(R.id.ipsec_ca).setVisibility(View.GONE);
|
||||||
|
|
||||||
|
// Then, unhide type-specific fields.
|
||||||
|
switch (type) {
|
||||||
|
case VpnProfile.TYPE_PPTP:
|
||||||
|
mMppe.setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
|
||||||
|
mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE);
|
||||||
|
// fall through
|
||||||
|
case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
|
||||||
|
mView.findViewById(R.id.ipsec_psk).setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VpnProfile.TYPE_L2TP_IPSEC_RSA:
|
||||||
|
mView.findViewById(R.id.l2tp).setVisibility(View.VISIBLE);
|
||||||
|
// fall through
|
||||||
|
case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
|
||||||
|
mView.findViewById(R.id.ipsec_ca).setVisibility(View.VISIBLE);
|
||||||
|
// fall through
|
||||||
|
case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
|
||||||
|
mView.findViewById(R.id.ipsec_user).setVisibility(View.VISIBLE);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean validate(boolean editing) {
|
||||||
|
if (!editing) {
|
||||||
|
return mUsername.getText().length() != 0 && mPassword.getText().length() != 0;
|
||||||
|
}
|
||||||
|
if (mName.getText().length() == 0 || mServer.getText().length() == 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
switch (mType.getSelectedItemPosition()) {
|
||||||
|
case VpnProfile.TYPE_PPTP:
|
||||||
|
return true;
|
||||||
|
|
||||||
|
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
|
||||||
|
case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
|
||||||
|
return mIpsecSecret.getText().length() != 0;
|
||||||
|
|
||||||
|
case VpnProfile.TYPE_L2TP_IPSEC_RSA:
|
||||||
|
case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
|
||||||
|
case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
|
||||||
|
return mIpsecUserCert.getSelectedItemPosition() != 0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void loadCertificates(Spinner spinner, String prefix, int firstId, String selected) {
|
||||||
|
Context context = getContext();
|
||||||
|
String first = (firstId == 0) ? "" : context.getString(firstId);
|
||||||
|
String[] certs = mKeyStore.saw(prefix);
|
||||||
|
|
||||||
|
if (certs == null || certs.length == 0) {
|
||||||
|
certs = new String[] {first};
|
||||||
|
} else {
|
||||||
|
String[] array = new String[certs.length + 1];
|
||||||
|
array[0] = first;
|
||||||
|
System.arraycopy(certs, 0, array, 1, certs.length);
|
||||||
|
certs = array;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
|
||||||
|
context, android.R.layout.simple_spinner_item, certs);
|
||||||
|
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||||
|
spinner.setAdapter(adapter);
|
||||||
|
|
||||||
|
for (int i = 1; i < certs.length; ++i) {
|
||||||
|
if (certs[i].equals(selected)) {
|
||||||
|
spinner.setSelection(i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean isEditing() {
|
||||||
|
return mEditing;
|
||||||
|
}
|
||||||
|
|
||||||
|
VpnProfile getProfile() {
|
||||||
|
// First, save common fields.
|
||||||
|
VpnProfile profile = new VpnProfile(mProfile.key);
|
||||||
|
profile.name = mName.getText().toString();
|
||||||
|
profile.type = mType.getSelectedItemPosition();
|
||||||
|
profile.server = mServer.getText().toString().trim();
|
||||||
|
profile.username = mUsername.getText().toString();
|
||||||
|
profile.password = getSecret(mPassword);
|
||||||
|
profile.domains = mDomains.getText().toString().trim();
|
||||||
|
profile.routes = mRoutes.getText().toString().trim();
|
||||||
|
|
||||||
|
// Then, save type-specific fields.
|
||||||
|
switch (profile.type) {
|
||||||
|
case VpnProfile.TYPE_PPTP:
|
||||||
|
profile.mppe = mMppe.isChecked();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VpnProfile.TYPE_L2TP_IPSEC_PSK:
|
||||||
|
profile.l2tpSecret = getSecret(mL2tpSecret);
|
||||||
|
// fall through
|
||||||
|
case VpnProfile.TYPE_IPSEC_XAUTH_PSK:
|
||||||
|
profile.ipsecSecret = getSecret(mIpsecSecret);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case VpnProfile.TYPE_L2TP_IPSEC_RSA:
|
||||||
|
profile.l2tpSecret = getSecret(mL2tpSecret);
|
||||||
|
// fall through
|
||||||
|
case VpnProfile.TYPE_IPSEC_XAUTH_RSA:
|
||||||
|
if (mIpsecCaCert.getSelectedItemPosition() != 0) {
|
||||||
|
profile.ipsecCaCert = (String) mIpsecCaCert.getSelectedItem();
|
||||||
|
}
|
||||||
|
// fall through
|
||||||
|
case VpnProfile.TYPE_IPSEC_HYBRID_RSA:
|
||||||
|
if (mIpsecUserCert.getSelectedItemPosition() != 0) {
|
||||||
|
profile.ipsecUserCert = (String) mIpsecUserCert.getSelectedItem();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
profile.saveLogin = mSaveLogin.isChecked();
|
||||||
|
return profile;
|
||||||
|
}
|
||||||
|
}
|
115
src/com/android/settings/vpn2/VpnProfile.java
Normal file
115
src/com/android/settings/vpn2/VpnProfile.java
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.vpn2;
|
||||||
|
|
||||||
|
import java.nio.charset.Charsets;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parcel-like entity class for VPN profiles. To keep things simple, all
|
||||||
|
* fields are package private. Methods are provided for serialization, so
|
||||||
|
* storage can be implemented easily. Two rules are set for this class.
|
||||||
|
* First, all fields must be kept non-null. Second, always make a copy
|
||||||
|
* using clone() before modifying.
|
||||||
|
*/
|
||||||
|
class VpnProfile implements Cloneable {
|
||||||
|
// Match these constants with R.array.vpn_types.
|
||||||
|
static final int TYPE_PPTP = 0;
|
||||||
|
static final int TYPE_L2TP_IPSEC_PSK = 1;
|
||||||
|
static final int TYPE_L2TP_IPSEC_RSA = 2;
|
||||||
|
static final int TYPE_IPSEC_XAUTH_PSK = 3;
|
||||||
|
static final int TYPE_IPSEC_XAUTH_RSA = 4;
|
||||||
|
static final int TYPE_IPSEC_HYBRID_RSA = 5;
|
||||||
|
static final int TYPE_MAX = 5;
|
||||||
|
|
||||||
|
// Entity fields.
|
||||||
|
final String key; // -1
|
||||||
|
String name = ""; // 0
|
||||||
|
int type = TYPE_PPTP; // 1
|
||||||
|
String server = ""; // 2
|
||||||
|
String username = ""; // 3
|
||||||
|
String password = ""; // 4
|
||||||
|
String domains = ""; // 5
|
||||||
|
String routes = ""; // 6
|
||||||
|
boolean mppe = false; // 7
|
||||||
|
String l2tpSecret = ""; // 8
|
||||||
|
String ipsecIdentifier = "";// 9
|
||||||
|
String ipsecSecret = ""; // 10
|
||||||
|
String ipsecUserCert = ""; // 11
|
||||||
|
String ipsecCaCert = ""; // 12
|
||||||
|
|
||||||
|
// Helper fields.
|
||||||
|
boolean saveLogin = false;
|
||||||
|
|
||||||
|
VpnProfile(String key) {
|
||||||
|
this.key = key;
|
||||||
|
}
|
||||||
|
|
||||||
|
static VpnProfile decode(String key, byte[] value) {
|
||||||
|
try {
|
||||||
|
if (key == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] values = new String(value, Charsets.UTF_8).split("\0", -1);
|
||||||
|
// Currently it always has 13 fields.
|
||||||
|
if (values.length < 13) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
VpnProfile profile = new VpnProfile(key);
|
||||||
|
profile.name = values[0];
|
||||||
|
profile.type = Integer.valueOf(values[1]);
|
||||||
|
if (profile.type < 0 || profile.type > TYPE_MAX) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
profile.server = values[2];
|
||||||
|
profile.username = values[3];
|
||||||
|
profile.password = values[4];
|
||||||
|
profile.domains = values[5];
|
||||||
|
profile.routes = values[6];
|
||||||
|
profile.mppe = Boolean.valueOf(values[7]);
|
||||||
|
profile.l2tpSecret = values[8];
|
||||||
|
profile.ipsecIdentifier = values[9];
|
||||||
|
profile.ipsecSecret = values[10];
|
||||||
|
profile.ipsecUserCert = values[11];
|
||||||
|
profile.ipsecCaCert = values[12];
|
||||||
|
|
||||||
|
profile.saveLogin = !profile.username.isEmpty() || !profile.password.isEmpty();
|
||||||
|
return profile;
|
||||||
|
} catch (Exception e) {
|
||||||
|
// ignore
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] encode() {
|
||||||
|
StringBuilder builder = new StringBuilder(name);
|
||||||
|
builder.append('\0').append(type);
|
||||||
|
builder.append('\0').append(server);
|
||||||
|
builder.append('\0').append(saveLogin ? username : "");
|
||||||
|
builder.append('\0').append(saveLogin ? password : "");
|
||||||
|
builder.append('\0').append(domains);
|
||||||
|
builder.append('\0').append(routes);
|
||||||
|
builder.append('\0').append(mppe);
|
||||||
|
builder.append('\0').append(l2tpSecret);
|
||||||
|
builder.append('\0').append(ipsecIdentifier);
|
||||||
|
builder.append('\0').append(ipsecSecret);
|
||||||
|
builder.append('\0').append(ipsecUserCert);
|
||||||
|
builder.append('\0').append(ipsecCaCert);
|
||||||
|
return builder.toString().getBytes(Charsets.UTF_8);
|
||||||
|
}
|
||||||
|
}
|
347
src/com/android/settings/vpn2/VpnSettings.java
Normal file
347
src/com/android/settings/vpn2/VpnSettings.java
Normal file
@@ -0,0 +1,347 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2011 The Android Open Source Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.android.settings.vpn2;
|
||||||
|
|
||||||
|
import com.android.settings.R;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.Handler;
|
||||||
|
import android.os.Message;
|
||||||
|
import android.preference.Preference;
|
||||||
|
import android.preference.PreferenceGroup;
|
||||||
|
import android.security.Credentials;
|
||||||
|
import android.security.KeyStore;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.ContextMenu;
|
||||||
|
import android.view.ContextMenu.ContextMenuInfo;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||||
|
|
||||||
|
import com.android.settings.SettingsPreferenceFragment;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
public class VpnSettings extends SettingsPreferenceFragment implements
|
||||||
|
Handler.Callback, Preference.OnPreferenceClickListener,
|
||||||
|
DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
|
||||||
|
|
||||||
|
private static final String TAG = "VpnSettings";
|
||||||
|
|
||||||
|
// Match these constants with R.array.vpn_states.
|
||||||
|
private static final int STATE_NONE = -1;
|
||||||
|
private static final int STATE_CONNECTING = 0;
|
||||||
|
private static final int STATE_CONNECTED = 1;
|
||||||
|
private static final int STATE_DISCONNECTED = 2;
|
||||||
|
private static final int STATE_FAILED = 3;
|
||||||
|
|
||||||
|
private final KeyStore mKeyStore = KeyStore.getInstance();
|
||||||
|
private boolean mUnlocking = false;
|
||||||
|
|
||||||
|
private HashMap<String, VpnPreference> mPreferences;
|
||||||
|
private VpnDialog mDialog;
|
||||||
|
private String mSelectedKey;
|
||||||
|
private Handler mHandler;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreate(Bundle savedState) {
|
||||||
|
super.onCreate(savedState);
|
||||||
|
addPreferencesFromResource(R.xml.vpn_settings2);
|
||||||
|
PreferenceGroup group = getPreferenceScreen();
|
||||||
|
group.setOrderingAsAdded(false);
|
||||||
|
group.findPreference("add_network").setOnPreferenceClickListener(this);
|
||||||
|
|
||||||
|
if (savedState != null) {
|
||||||
|
VpnProfile profile = VpnProfile.decode(savedState.getString("VpnKey"),
|
||||||
|
savedState.getByteArray("VpnProfile"));
|
||||||
|
if (profile != null) {
|
||||||
|
mDialog = new VpnDialog(getActivity(), this, profile,
|
||||||
|
savedState.getBoolean("VpnEditing"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle savedState) {
|
||||||
|
// We do not save view hierarchy, as they are just profiles.
|
||||||
|
if (mDialog != null) {
|
||||||
|
VpnProfile profile = mDialog.getProfile();
|
||||||
|
savedState.putString("VpnKey", profile.key);
|
||||||
|
savedState.putByteArray("VpnProfile", profile.encode());
|
||||||
|
savedState.putBoolean("VpnEditing", mDialog.isEditing());
|
||||||
|
}
|
||||||
|
// else?
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume() {
|
||||||
|
super.onResume();
|
||||||
|
|
||||||
|
// Check KeyStore here, so others do not need to deal with it.
|
||||||
|
if (mKeyStore.state() != KeyStore.State.UNLOCKED) {
|
||||||
|
if (!mUnlocking) {
|
||||||
|
// Let us unlock KeyStore. See you later!
|
||||||
|
Credentials.getInstance().unlock(getActivity());
|
||||||
|
} else {
|
||||||
|
// We already tried, but it is still not working!
|
||||||
|
getActivity().getFragmentManager().popBackStack();
|
||||||
|
}
|
||||||
|
mUnlocking = !mUnlocking;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Now KeyStore is always unlocked. Reset the flag.
|
||||||
|
mUnlocking = false;
|
||||||
|
|
||||||
|
// Currently we are the only user of profiles in KeyStore.
|
||||||
|
// Assuming KeyStore and KeyGuard do the right thing, we can
|
||||||
|
// safely cache profiles in the memory.
|
||||||
|
if (mPreferences == null) {
|
||||||
|
mPreferences = new HashMap<String, VpnPreference>();
|
||||||
|
|
||||||
|
String[] keys = mKeyStore.saw(Credentials.VPN);
|
||||||
|
if (keys != null && keys.length > 0) {
|
||||||
|
Context context = getActivity();
|
||||||
|
|
||||||
|
for (String key : keys) {
|
||||||
|
VpnProfile profile = VpnProfile.decode(key,
|
||||||
|
mKeyStore.get(Credentials.VPN + key));
|
||||||
|
if (profile == null) {
|
||||||
|
Log.w(TAG, "bad profile: key = " + key);
|
||||||
|
mKeyStore.delete(Credentials.VPN + key);
|
||||||
|
} else {
|
||||||
|
VpnPreference preference = new VpnPreference(context, profile);
|
||||||
|
mPreferences.put(key, preference);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
PreferenceGroup group = getPreferenceScreen();
|
||||||
|
for (VpnPreference preference : mPreferences.values()) {
|
||||||
|
group.addPreference(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show the dialog if there is one.
|
||||||
|
if (mDialog != null) {
|
||||||
|
mDialog.setOnDismissListener(this);
|
||||||
|
mDialog.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start monitoring.
|
||||||
|
if (mHandler == null) {
|
||||||
|
mHandler = new Handler(this);
|
||||||
|
}
|
||||||
|
mHandler.sendEmptyMessage(0);
|
||||||
|
|
||||||
|
// Register for context menu. Hmmm, getListView() is hidden?
|
||||||
|
registerForContextMenu(getListView());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
|
||||||
|
// Hide the dialog if there is one.
|
||||||
|
if (mDialog != null) {
|
||||||
|
mDialog.setOnDismissListener(null);
|
||||||
|
mDialog.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Unregister for context menu.
|
||||||
|
unregisterForContextMenu(getListView());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDismiss(DialogInterface dialog) {
|
||||||
|
// Here is the exit of a dialog.
|
||||||
|
mDialog = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int button) {
|
||||||
|
if (button == DialogInterface.BUTTON_POSITIVE) {
|
||||||
|
// Always save the profile.
|
||||||
|
VpnProfile profile = mDialog.getProfile();
|
||||||
|
mKeyStore.put(Credentials.VPN + profile.key, profile.encode());
|
||||||
|
|
||||||
|
// Update the preference.
|
||||||
|
VpnPreference preference = mPreferences.get(profile.key);
|
||||||
|
if (preference != null) {
|
||||||
|
disconnect(profile.key);
|
||||||
|
preference.update(profile);
|
||||||
|
} else {
|
||||||
|
preference = new VpnPreference(getActivity(), profile);
|
||||||
|
mPreferences.put(profile.key, preference);
|
||||||
|
getPreferenceScreen().addPreference(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are not editing, connect!
|
||||||
|
if (!mDialog.isEditing()) {
|
||||||
|
connect(profile.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo info) {
|
||||||
|
if (mDialog != null) {
|
||||||
|
Log.v(TAG, "onCreateContextMenu() is called when mDialog != null");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (info instanceof AdapterContextMenuInfo) {
|
||||||
|
Preference preference = (Preference) getListView().getItemAtPosition(
|
||||||
|
((AdapterContextMenuInfo) info).position);
|
||||||
|
if (preference instanceof VpnPreference) {
|
||||||
|
VpnProfile profile = ((VpnPreference) preference).getProfile();
|
||||||
|
mSelectedKey = profile.key;
|
||||||
|
menu.setHeaderTitle(profile.name);
|
||||||
|
menu.add(Menu.NONE, R.string.vpn_menu_edit, 0, R.string.vpn_menu_edit);
|
||||||
|
menu.add(Menu.NONE, R.string.vpn_menu_delete, 0, R.string.vpn_menu_delete);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onContextItemSelected(MenuItem item) {
|
||||||
|
if (mDialog != null) {
|
||||||
|
Log.v(TAG, "onContextItemSelected() is called when mDialog != null");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
VpnPreference preference = mPreferences.get(mSelectedKey);
|
||||||
|
if (preference == null) {
|
||||||
|
Log.v(TAG, "onContextItemSelected() is called but no preference is found");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.string.vpn_menu_edit:
|
||||||
|
mDialog = new VpnDialog(getActivity(), this, preference.getProfile(), true);
|
||||||
|
mDialog.setOnDismissListener(this);
|
||||||
|
mDialog.show();
|
||||||
|
return true;
|
||||||
|
case R.string.vpn_menu_delete:
|
||||||
|
disconnect(mSelectedKey);
|
||||||
|
getPreferenceScreen().removePreference(preference);
|
||||||
|
mPreferences.remove(mSelectedKey);
|
||||||
|
mKeyStore.delete(Credentials.VPN + mSelectedKey);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceClick(Preference preference) {
|
||||||
|
if (mDialog != null) {
|
||||||
|
Log.v(TAG, "onPreferenceClick() is called when mDialog != null");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (preference instanceof VpnPreference) {
|
||||||
|
mDialog = new VpnDialog(getActivity(), this,
|
||||||
|
((VpnPreference) preference).getProfile(), false);
|
||||||
|
} else {
|
||||||
|
// Generate a new key. Here we just use the current time.
|
||||||
|
long millis = System.currentTimeMillis();
|
||||||
|
while (mPreferences.containsKey(Long.toHexString(millis))) {
|
||||||
|
++millis;
|
||||||
|
}
|
||||||
|
mDialog = new VpnDialog(getActivity(), this,
|
||||||
|
new VpnProfile(Long.toHexString(millis)), true);
|
||||||
|
}
|
||||||
|
mDialog.setOnDismissListener(this);
|
||||||
|
mDialog.show();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean handleMessage(Message message) {
|
||||||
|
mHandler.removeMessages(0);
|
||||||
|
|
||||||
|
if (isResumed()) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
mHandler.sendEmptyMessageDelayed(0, 1000);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void connect(String key) {
|
||||||
|
}
|
||||||
|
|
||||||
|
private void disconnect(String key) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private class VpnPreference extends Preference {
|
||||||
|
private VpnProfile mProfile;
|
||||||
|
private int mState = STATE_NONE;
|
||||||
|
|
||||||
|
VpnPreference(Context context, VpnProfile profile) {
|
||||||
|
super(context);
|
||||||
|
setPersistent(false);
|
||||||
|
setOnPreferenceClickListener(VpnSettings.this);
|
||||||
|
|
||||||
|
mProfile = profile;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
VpnProfile getProfile() {
|
||||||
|
return mProfile;
|
||||||
|
}
|
||||||
|
|
||||||
|
void update(VpnProfile profile) {
|
||||||
|
mProfile = profile;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void update() {
|
||||||
|
if (mState != STATE_NONE) {
|
||||||
|
String[] states = getContext().getResources()
|
||||||
|
.getStringArray(R.array.vpn_states);
|
||||||
|
setSummary(states[mState]);
|
||||||
|
} else {
|
||||||
|
String[] types = getContext().getResources()
|
||||||
|
.getStringArray(R.array.vpn_types_long);
|
||||||
|
setSummary(types[mProfile.type]);
|
||||||
|
}
|
||||||
|
setTitle(mProfile.name);
|
||||||
|
notifyChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(Preference preference) {
|
||||||
|
int result = 1;
|
||||||
|
if (preference instanceof VpnPreference) {
|
||||||
|
VpnPreference another = (VpnPreference) preference;
|
||||||
|
|
||||||
|
if ((result = another.mState - mState) == 0 &&
|
||||||
|
(result = mProfile.name.compareTo(another.mProfile.name)) == 0 &&
|
||||||
|
(result = mProfile.type - another.mProfile.type) == 0) {
|
||||||
|
result = mProfile.key.compareTo(another.mProfile.key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Reference in New Issue
Block a user