Add L2TP secret, L2TP/IPSec PSK support. Fix screen orientation.

* Changes
  + Add L2tpActor, L2tpEditor, L2tpIpsecPskActor.
  + Make L2tpIpsecEditor extend L2tpEditor.
  + Revise the code for saving username. Make
    VpnSettings.saveProfileToStorage() static.
  + Fix support for screen orientation change in both VpnSettings and
    VpnEditor.

  Patch Set 2:
  + Remove Util.isNullOrEmpty(). Use TextUtils.isEmpty() instead.
  + Remove unused imports. Wrap lines longer than 80 chars.

  Patch Set 3:
  + Fix all the strings according to UI feedback.
  + Remove all the added actor subclasses and move password to editor.
  + Remove VPN entry in Security & location.

  Patch Set 4:
  + Misc string fixes.

  Patch Set 5:
  + Add strings for credential storage settings.
  + Changed the error dialog icon.
  + Fix "Remember me" indentation in connect dialog.

  Patch Set 6:
  + resolve res/values/strings.xml
This commit is contained in:
Hung-ying Tyan
2009-06-26 14:24:50 +08:00
parent 386278a338
commit e7565f3c48
15 changed files with 660 additions and 369 deletions

View File

@@ -1,54 +1,69 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" Licensed under the Apache License, Version 2.0 (the "License");
android:orientation="vertical" 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.
-->
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="fill_parent"> android:layout_height="wrap_content">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:orientation="vertical"
android:layout_marginLeft="@dimen/vpn_connect_margin_left"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content"> android:layout_height="fill_parent"
<TextView android:id="@+id/username_str" android:padding="10dip">
android:layout_width="@dimen/vpn_connect_input_box_label_width"
android:layout_height="wrap_content" <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:textSize="@dimen/vpn_connect_normal_text_size" android:orientation="horizontal"
android:gravity="right" android:layout_width="fill_parent"
android:layout_marginRight="@dimen/vpn_connect_input_box_padding" android:layout_height="wrap_content">
android:text="@string/vpn_username_colon" /> <TextView android:id="@+id/username_str"
<EditText android:id="@+id/username_value" android:layout_width="@dimen/vpn_connect_input_box_label_width"
android:layout_height="wrap_content"
android:textSize="@dimen/vpn_connect_normal_text_size"
android:gravity="right"
android:layout_marginRight="@dimen/vpn_connect_margin_right"
android:text="@string/vpn_username_colon" />
<EditText android:id="@+id/username_value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:singleLine="True"/>
</LinearLayout>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView android:id="@+id/password_str"
android:layout_width="@dimen/vpn_connect_input_box_label_width"
android:layout_height="wrap_content"
android:textSize="@dimen/vpn_connect_normal_text_size"
android:gravity="right"
android:layout_marginRight="@dimen/vpn_connect_margin_right"
android:text="@string/vpn_password_colon" />
<EditText android:id="@+id/password_value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:password="True"
android:singleLine="True"/>
</LinearLayout>
<CheckBox android:id="@+id/save_username"
android:layout_width="fill_parent" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginRight="@dimen/vpn_connect_margin_right" android:layout_marginLeft="66dip"
android:singleLine="True"/> android:text="@string/vpn_save_username" />
</LinearLayout> </LinearLayout>
</ScrollView>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_marginLeft="@dimen/vpn_connect_margin_left"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="10sp">
<TextView android:id="@+id/password_str"
android:layout_width="@dimen/vpn_connect_input_box_label_width"
android:layout_height="wrap_content"
android:textSize="@dimen/vpn_connect_normal_text_size"
android:gravity="right"
android:layout_marginRight="@dimen/vpn_connect_input_box_padding"
android:text="@string/vpn_password_colon" />
<EditText android:id="@+id/password_value"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/vpn_connect_margin_right"
android:password="True"
android:singleLine="True"/>
</LinearLayout>
<CheckBox android:id="@+id/save_username"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/vpn_connect_margin_left"
android:text="@string/vpn_save_username" />
</LinearLayout>

View File

@@ -1,9 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<dimen name="vpn_connect_margin_left">5sp</dimen> <dimen name="vpn_connect_margin_right">10sp</dimen>
<dimen name="vpn_connect_margin_right">5sp</dimen>
<dimen name="vpn_connect_normal_text_size">16sp</dimen> <dimen name="vpn_connect_normal_text_size">16sp</dimen>
<dimen name="vpn_connect_input_box_label_width">90sp</dimen> <dimen name="vpn_connect_input_box_label_width">90sp</dimen>
<dimen name="vpn_connect_input_box_width">200sp</dimen>
<dimen name="vpn_connect_input_box_padding">5sp</dimen>
</resources> </resources>

View File

@@ -1817,74 +1817,150 @@ 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>
<string name="vpn_username_colon">User name:</string> <!-- Title of VPN connect dialog -->
<string name="vpn_connect_to">Connect to %s</string>
<!-- In VPN connect dialog, for inputing username and password -->
<string name="vpn_username_colon">Username:</string>
<string name="vpn_password_colon">Password:</string> <string name="vpn_password_colon">Password:</string>
<string name="vpn_username">User name</string> <string name="vpn_username">username</string>
<string name="vpn_password">Password</string> <string name="vpn_password">password</string>
<!-- In VPN connect dialog where user may check to remember the username entered -->
<string name="vpn_save_username">Remember me</string> <string name="vpn_save_username">Remember me</string>
<string name="vpn_you_miss_a_field">You missed a field!</string>
<string name="vpn_please_fill_up">Please fill up \"%s\".</string>
<string name="vpn_connect_button">Connect</string> <string name="vpn_connect_button">Connect</string>
<string name="vpn_cancel_button">Cancel</string>
<string name="vpn_yes_button">Yes</string> <string name="vpn_yes_button">Yes</string>
<string name="vpn_no_button">No</string> <string name="vpn_no_button">No</string>
<string name="vpn_back_button">Back</string> <string name="vpn_back_button">Back</string>
<string name="vpn_mistake_button">No, it's a mistake</string> <string name="vpn_mistake_button">No, it's a mistake</string>
<string name="vpn_menu_save">Save</string> <string name="vpn_menu_done">Save</string>
<!-- Edit VPN screen menu option to discard the user's changes for this VPN --> <string name="vpn_menu_cancel">Cancel</string>
<string name="vpn_menu_cancel">Discard</string> <string name="vpn_menu_revert">Revert</string>
<string name="vpn_menu_connect">Connect</string> <string name="vpn_menu_connect">Connect to network</string>
<string name="vpn_menu_disconnect">Disconnect</string> <string name="vpn_menu_disconnect">Disconnect network</string>
<string name="vpn_menu_edit">Edit</string> <string name="vpn_menu_edit">Edit network</string>
<string name="vpn_menu_delete">Delete</string> <string name="vpn_menu_delete">Delete network</string>
<!-- VPN error dialog title --> <!-- VPN error dialog messages -->
<string name="vpn_error_title">Attention</string> <string name="vpn_error_miss_entering">You must enter a %s.</string>
<string name="vpn_error_name_empty">VPN Name cannot be empty.</string> <string name="vpn_error_miss_selecting">You must select a %s.</string>
<string name="vpn_error_server_name_empty">The Server Name field cannot be empty.</string> <string name="vpn_error_duplicate_name">The VPN name \'%s\' already exists. Find another name.</string>
<string name="vpn_error_duplicate_name">The VPN Name \'%s\' already exists. Find another name.</string> <string name="vpn_confirm_profile_deletion">Are you sure you want to delete this VPN?</string>
<string name="vpn_error_user_certificate_not_selected">Need to select a user certificate.</string> <string name="vpn_confirm_add_profile_cancellation">Are you sure you don\'t want to create this profile?</string>
<string name="vpn_error_ca_certificate_not_selected">Need to select a CA certificate.</string> <string name="vpn_confirm_edit_profile_cancellation">Are you sure you want to discard the changes made to this profile?</string>
<string name="vpn_error_userkey_not_selected">Need to select a userkey.</string>
<string name="vpn_confirm_profile_cancellation">Are you sure you don\'t want to create this profile?</string>
<string name="vpn_confirm_reconnect">The previous connection attempt failed. Do you want to try again?</string> <string name="vpn_confirm_reconnect">The previous connection attempt failed. Do you want to try again?</string>
<string name="vpn_add_new_vpn">Add new VPN</string> <!-- VPN type selection activity title -->
<string name="vpn_edit_title_add">Add new %s VPN</string> <string name="vpn_type_title">Add VPN</string>
<string name="vpn_edit_title_edit">Edit %s VPN</string> <!-- "Add VPN" preference title -->
<string name="vpn_type_title">Select VPN type</string> <string name="vpn_add_new_vpn">Add VPN</string>
<string name="vpns">VPN networks</string> <!-- VPN profile editor title when adding a new profile -->
<!-- EditTextPreference summary text when no value has been set --> <string name="vpn_edit_title_add">Add %s VPN</string>
<string name="vpn_not_set">Click to set the value</string> <!-- VPN profile editor title when editing an existing profile -->
<!-- EditTextPreference summary text when VPN is connecting --> <string name="vpn_edit_title_edit">%s details</string>
<!-- Preference group title for a list of VPN profiles -->
<string name="vpns">VPNs</string>
<!-- Preference summary text when VPN is connecting -->
<string name="vpn_connecting">Connecting...</string> <string name="vpn_connecting">Connecting...</string>
<!-- EditTextPreference summary text when VPN is disconnecting --> <!-- Preference summary text when VPN is disconnecting -->
<string name="vpn_disconnecting">Disconnecting...</string> <string name="vpn_disconnecting">Disconnecting...</string>
<!-- EditTextPreference summary text when VPN is connected --> <!-- Preference summary text when VPN is connected -->
<string name="vpn_connected">Connected</string> <string name="vpn_connected">Connected</string>
<!-- EditTextPreference summary text when VPN is not connected --> <!-- Preference summary text when VPN is not connected -->
<string name="vpn_connect_hint">Select to connect</string> <string name="vpn_connect_hint">Connect to network</string>
<!-- dialog title when asking for username and password -->
<string name="vpn_connect_to">Connect to %s</string>
<string name="vpn_default_profile_name">nowhere</string> <string name="vpn_default_profile_name">nowhere</string>
<string name="vpn_name">VPN Name</string> <!-- Name of a VPN profile -->
<string name="vpn_name_summary">Give a name to this VPN</string> <string name="vpn_name">VPN name</string>
<string name="vpn_profile_added">'%s' is added</string> <!-- Toast message shown when a profile is added -->
<string name="vpn_profile_replaced">Changes are made to '%s'</string> <string name="vpn_profile_added">&#39;%s&#39; is added</string>
<!-- Toast message shown when changes of a profile is saved -->
<string name="vpn_profile_replaced">Changes are made to &#39;%s&#39;</string>
<string name="vpn_user_certificate_title">User Certificate</string> <!-- Preference title -->
<string name="vpn_ca_certificate_title">CA Certificate</string> <string name="vpn_user_certificate_title">Set user certificate</string>
<string name="vpn_userkey_title">User Key</string> <!-- Complete term -->
<string name="vpn_server_name_title">Server Name</string> <string name="vpn_user_certificate">User certificate</string>
<string name="vpn_dns_search_list_title">DNS Search List</string>
<string name="vpn_settings_category">VPN</string> <!-- Preference title -->
<string name="vpn_settings_title">VPN</string> <string name="vpn_ca_certificate_title">Set CA certificate</string>
<string name="vpn_settings_summary">Set up &amp; manage VPN configurations, connections</string> <!-- Complete term -->
<string name="vpn_ca_certificate">Certificate authority (CA) certificate</string>
<!-- Preference title -->
<string name="vpn_l2tp_secret_string_title">Set L2TP secret</string>
<!-- Complete term -->
<string name="vpn_l2tp_secret">L2TP secret</string>
<!-- Preference title -->
<string name="vpn_psk_title">Set IPSec pre-shared key</string>
<!-- Complete term -->
<string name="vpn_psk">IPSec pre-shared key</string>
<!-- Preference title -->
<string name="vpn_vpn_server_title">Set VPN server</string>
<!-- Complete term -->
<string name="vpn_vpn_server">VPN server</string>
<!-- Dialog title for setting VPN server name -->
<string name="vpn_vpn_server_dialog_title">VPN server name</string>
<!-- Preference title -->
<string name="vpn_dns_search_list_title">DNS search domains</string>
<!-- Complete term -->
<string name="vpn_dns_search_list">DNS search domains</string>
<!-- Summary text to hint that the value is set -->
<string name="vpn_field_is_set">%s is set</string>
<!-- Summary text to hint that the value is not set -->
<string name="vpn_field_not_set">%s not set</string>
<!-- Summary text to hint that the value is not set but it's not required-->
<string name="vpn_field_not_set_optional">%s not set (optional)</string>
<!-- CheckBoxPreference title to enable something -->
<string name="vpn_enable_field">Enable %s</string>
<!-- CheckBoxPreference title to disable something -->
<string name="vpn_disable_field">Disable %s</string>
<!-- CheckBoxPreference summary to hint that something is enabled -->
<string name="vpn_is_enabled">%s is enabled</string>
<!-- CheckBoxPreference summary to hint that something is disabled -->
<string name="vpn_is_disabled">%s is disabled</string>
<!-- Title of preference to enter the VPN settings activity -->
<string name="vpn_settings_title">VPN settings</string>
<!-- Summary of preference to enter the VPN settings activity -->
<string name="vpn_settings_summary">Set up &amp; manage Virtual Private Networks (VPNs)</string>
<!-- Title of preference group for credential storage settings -->
<string name="cstor_settings_category">Credential storage</string>
<!-- Title of preference to enable/dislable access to credential storage -->
<string name="cstor_access_title">Use secure credentials</string>
<!-- Summary of preference to enable/dislable access to credential storage -->
<string name="cstor_access_summary">Allow applications to access secure certificates and other credentials</string>
<!-- Title of preference to set storage password -->
<string name="cstor_set_passwd_title">Set storage password</string>
<!-- Summary of preference to set storage password -->
<string name="cstor_set_passwd_summary">Set or change the secure credential storage password</string>
<!-- Title of dialog to set storage password -->
<string name="cstor_set_passwd_dialog_title">Set password</string>
<!-- Title of preference to reset storage -->
<string name="cstor_reset_title">Clear storage</string>
<!-- Summary of preference to reset storage -->
<string name="cstor_reset_summary">Clear credential storage of all contents and reset its password</string>
<string name="cstor_reset_hint">Are you sure you want to delete all certificates and other stored credentials and reset the password?</string>
<!-- Description for the old-password input box -->
<string name="cstor_old_password">Current password:</string>
<!-- Description for the new-password input box -->
<string name="cstor_new_password">New password:</string>
<!-- Description for the confirm-new-password input box -->
<string name="cstor_confirm_password">Confirm new password:</string>
<!-- Description when user set up the storage for the very first time -->
<string name="cstor_first_time_hint">You must set a password for credential storage before you can store secure certificates and other credentials in it.</string>
<!-- Sound settings screen, setting check box label --> <!-- Sound settings screen, setting check box label -->
<string name="emergency_tone_title">Emergency tone</string> <string name="emergency_tone_title">Emergency tone</string>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project <!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@@ -18,10 +18,9 @@
xmlns:settings="http://schemas.android.com/apk/res/com.android.settings"> xmlns:settings="http://schemas.android.com/apk/res/com.android.settings">
<EditTextPreference <EditTextPreference
android:key="vpn_name"
android:title="@string/vpn_name" android:title="@string/vpn_name"
android:dialogTitle="@string/vpn_name" android:dialogTitle="@string/vpn_name"
android:key="vpn_name"
android:summary="@string/vpn_name_summary"
android:singleLine="true"/> android:singleLine="true"/>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project <!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2008 The Android Open Source Project <!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@@ -28,7 +28,6 @@ import android.content.Intent;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.database.Cursor; import android.database.Cursor;
import android.location.LocationManager; import android.location.LocationManager;
import android.net.vpn.VpnManager;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference; import android.preference.CheckBoxPreference;
import android.preference.Preference; import android.preference.Preference;
@@ -198,17 +197,6 @@ public class SecuritySettings extends PreferenceActivity implements
showPassword.setPersistent(false); showPassword.setPersistent(false);
passwordsCat.addPreference(showPassword); passwordsCat.addPreference(showPassword);
PreferenceScreen vpnPreferences = getPreferenceManager()
.createPreferenceScreen(this);
vpnPreferences.setTitle(R.string.vpn_settings_title);
vpnPreferences.setSummary(R.string.vpn_settings_summary);
vpnPreferences.setIntent(new VpnManager(this).createSettingsActivityIntent());
PreferenceCategory vpnCat = new PreferenceCategory(this);
vpnCat.setTitle(R.string.vpn_settings_category);
root.addPreference(vpnCat);
vpnCat.addPreference(vpnPreferences);
return root; return root;
} }

View File

@@ -28,11 +28,14 @@ import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState; import android.net.vpn.VpnState;
import android.os.IBinder; import android.os.IBinder;
import android.os.RemoteException; import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.View; import android.view.View;
import android.widget.CheckBox; import android.widget.CheckBox;
import android.widget.TextView; import android.widget.TextView;
import java.io.IOException;
/** /**
* A {@link VpnProfileActor} that provides an authentication view for users to * A {@link VpnProfileActor} that provides an authentication view for users to
* input username and password before connecting to the VPN server. * input username and password before connecting to the VPN server.
@@ -66,9 +69,9 @@ public class AuthenticationActor implements VpnProfileActor {
TextView usernameView = (TextView) d.findViewById(R.id.username_value); TextView usernameView = (TextView) d.findViewById(R.id.username_value);
TextView passwordView = (TextView) d.findViewById(R.id.password_value); TextView passwordView = (TextView) d.findViewById(R.id.password_value);
Context c = mContext; Context c = mContext;
if (Util.isNullOrEmpty(usernameView.getText().toString())) { if (TextUtils.isEmpty(usernameView.getText().toString())) {
return c.getString(R.string.vpn_username); return c.getString(R.string.vpn_username);
} else if (Util.isNullOrEmpty(passwordView.getText().toString())) { } else if (TextUtils.isEmpty(passwordView.getText().toString())) {
return c.getString(R.string.vpn_password); return c.getString(R.string.vpn_password);
} else { } else {
return null; return null;
@@ -81,12 +84,14 @@ public class AuthenticationActor implements VpnProfileActor {
TextView passwordView = (TextView) d.findViewById(R.id.password_value); TextView passwordView = (TextView) d.findViewById(R.id.password_value);
CheckBox saveUsername = (CheckBox) d.findViewById(R.id.save_username); CheckBox saveUsername = (CheckBox) d.findViewById(R.id.save_username);
// save username try {
if (saveUsername.isChecked()) { setSavedUsername(saveUsername.isChecked()
mProfile.setSavedUsername(usernameView.getText().toString()); ? usernameView.getText().toString()
} else { : "");
mProfile.setSavedUsername(""); } catch (IOException e) {
Log.e(TAG, "setSavedUsername()", e);
} }
connect(usernameView.getText().toString(), connect(usernameView.getText().toString(),
passwordView.getText().toString()); passwordView.getText().toString());
passwordView.setText(""); passwordView.setText("");
@@ -101,7 +106,11 @@ public class AuthenticationActor implements VpnProfileActor {
public void updateConnectView(Dialog d) { public void updateConnectView(Dialog d) {
String username = mProfile.getSavedUsername(); String username = mProfile.getSavedUsername();
if (username == null) username = ""; if (username == null) username = "";
updateConnectView(d, username, "", !Util.isNullOrEmpty(username)); updateConnectView(d, username, "", !TextUtils.isEmpty(username));
}
protected Context getContext() {
return mContext;
} }
private void connect(final String username, final String password) { private void connect(final String username, final String password) {
@@ -121,7 +130,6 @@ public class AuthenticationActor implements VpnProfileActor {
if (!success) { if (!success) {
Log.d(TAG, "~~~~~~ connect() failed!"); Log.d(TAG, "~~~~~~ connect() failed!");
// TODO: pop up a dialog
broadcastConnectivity(VpnState.IDLE); broadcastConnectivity(VpnState.IDLE);
} else { } else {
Log.d(TAG, "~~~~~~ connect() succeeded!"); Log.d(TAG, "~~~~~~ connect() succeeded!");
@@ -209,4 +217,11 @@ public class AuthenticationActor implements VpnProfileActor {
} catch (Exception e) {} } catch (Exception e) {}
} }
} }
private void setSavedUsername(String name) throws IOException {
if (!name.equals(mProfile.getSavedUsername())) {
mProfile.setSavedUsername(name);
VpnSettings.saveProfileToStorage(mProfile);
}
}
} }

View File

@@ -0,0 +1,119 @@
/*
* Copyright (C) 2009 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.vpn;
import com.android.settings.R;
import android.content.Context;
import android.net.vpn.L2tpProfile;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceGroup;
/**
* The class for editing {@link L2tpProfile}.
*/
class L2tpEditor extends VpnProfileEditor {
private CheckBoxPreference mSecret;
private EditTextPreference mSecretString;
private String mOriginalSecret;
private boolean mOriginalSecretEnabled;
public L2tpEditor(L2tpProfile p) {
super(p);
}
@Override
protected void loadExtraPreferencesTo(PreferenceGroup subpanel) {
Context c = subpanel.getContext();
subpanel.addPreference(createSecretPreference(c));
subpanel.addPreference(createSecretStringPreference(c));
mSecretString.setEnabled(mSecret.isChecked());
L2tpProfile profile = (L2tpProfile) getProfile();
mOriginalSecret = profile.getSecretString();
mOriginalSecretEnabled = profile.isSecretEnabled();
}
@Override
public String validate() {
String result = super.validate();
if (!mSecret.isChecked()) return result;
return ((result != null)
? result
: validate(mSecretString, R.string.vpn_l2tp_secret));
}
@Override
public void saveSecrets(String originalProfileName) {
L2tpProfile profile = (L2tpProfile) getProfile();
// TODO: fill up the implementation after keystore is available
}
private Preference createSecretPreference(Context c) {
final L2tpProfile profile = (L2tpProfile) getProfile();
CheckBoxPreference secret = mSecret = new CheckBoxPreference(c);
boolean enabled = profile.isSecretEnabled();
setSecretTitle(secret, R.string.vpn_l2tp_secret, enabled);
secret.setChecked(enabled);
setSecretSummary(secret, enabled);
secret.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(
Preference pref, Object newValue) {
boolean enabled = (Boolean) newValue;
profile.setSecretEnabled(enabled);
mSecretString.setEnabled(enabled);
setSecretTitle(mSecret, R.string.vpn_l2tp_secret,
enabled);
setSecretSummary(mSecret, enabled);
return true;
}
});
return secret;
}
private Preference createSecretStringPreference(Context c) {
final L2tpProfile profile = (L2tpProfile) getProfile();
mSecretString = createSecretPreference(c,
R.string.vpn_l2tp_secret_string_title,
R.string.vpn_l2tp_secret,
profile.getSecretString(),
new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(
Preference pref, Object newValue) {
profile.setSecretString((String) newValue);
setSecretSummary(mSecretString,
R.string.vpn_l2tp_secret,
(String) newValue);
return true;
}
});
return mSecretString;
}
private void setSecretSummary(CheckBoxPreference secret, boolean enabled) {
Context c = secret.getContext();
String formatString = c.getString(enabled
? R.string.vpn_is_enabled
: R.string.vpn_is_disabled);
secret.setSummary(String.format(
formatString, c.getString(R.string.vpn_l2tp_secret)));
}
}

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2007 The Android Open Source Project * Copyright (C) 2009 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -25,11 +25,12 @@ import android.preference.ListPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceGroup; import android.preference.PreferenceGroup;
import android.security.Keystore; import android.security.Keystore;
import android.text.TextUtils;
/** /**
* The class for editing {@link L2tpIpsecProfile}. * The class for editing {@link L2tpIpsecProfile}.
*/ */
class L2tpIpsecEditor extends VpnProfileEditor { class L2tpIpsecEditor extends L2tpEditor {
private static final String TAG = L2tpIpsecEditor.class.getSimpleName(); private static final String TAG = L2tpIpsecEditor.class.getSimpleName();
private ListPreference mUserCertificate; private ListPreference mUserCertificate;
@@ -44,23 +45,22 @@ class L2tpIpsecEditor extends VpnProfileEditor {
@Override @Override
protected void loadExtraPreferencesTo(PreferenceGroup subpanel) { protected void loadExtraPreferencesTo(PreferenceGroup subpanel) {
super.loadExtraPreferencesTo(subpanel);
Context c = subpanel.getContext(); Context c = subpanel.getContext();
subpanel.addPreference(createUserCertificatePreference(c)); subpanel.addPreference(createUserCertificatePreference(c));
subpanel.addPreference(createCaCertificatePreference(c)); subpanel.addPreference(createCaCertificatePreference(c));
} }
@Override @Override
public String validate(Context c) { public String validate() {
String result = super.validate(c); String result = super.validate();
if (result != null) { if (result == null) {
return result; result = validate(mUserCertificate, R.string.vpn_user_certificate);
} else if (Util.isNullOrEmpty(mUserCertificate.getValue())) {
return c.getString(R.string.vpn_error_user_certificate_not_selected);
} else if (Util.isNullOrEmpty(mCaCertificate.getValue())) {
return c.getString(R.string.vpn_error_ca_certificate_not_selected);
} else {
return null;
} }
if (result == null) {
result = validate(mCaCertificate, R.string.vpn_ca_certificate);
}
return result;
} }
private Preference createUserCertificatePreference(Context c) { private Preference createUserCertificatePreference(Context c) {
@@ -72,9 +72,13 @@ class L2tpIpsecEditor extends VpnProfileEditor {
public boolean onPreferenceChange( public boolean onPreferenceChange(
Preference pref, Object newValue) { Preference pref, Object newValue) {
mProfile.setUserCertificate((String) newValue); mProfile.setUserCertificate((String) newValue);
return onPreferenceChangeCommon(pref, newValue); setSummary(pref, R.string.vpn_user_certificate,
(String) newValue);
return true;
} }
}); });
setSummary(mUserCertificate, R.string.vpn_user_certificate,
mProfile.getUserCertificate());
return mUserCertificate; return mUserCertificate;
} }
@@ -87,9 +91,13 @@ class L2tpIpsecEditor extends VpnProfileEditor {
public boolean onPreferenceChange( public boolean onPreferenceChange(
Preference pref, Object newValue) { Preference pref, Object newValue) {
mProfile.setCaCertificate((String) newValue); mProfile.setCaCertificate((String) newValue);
return onPreferenceChangeCommon(pref, newValue); setSummary(pref, R.string.vpn_ca_certificate,
(String) newValue);
return true;
} }
}); });
setSummary(mCaCertificate, R.string.vpn_ca_certificate,
mProfile.getCaCertificate());
return mCaCertificate; return mCaCertificate;
} }
@@ -103,13 +111,7 @@ class L2tpIpsecEditor extends VpnProfileEditor {
pref.setEntries(keys); pref.setEntries(keys);
pref.setEntryValues(keys); pref.setEntryValues(keys);
pref.setValue(text); pref.setValue(text);
pref.setSummary(checkNull(text, c));
pref.setOnPreferenceChangeListener(listener); pref.setOnPreferenceChangeListener(listener);
return pref; return pref;
} }
private boolean onPreferenceChangeCommon(Preference pref, Object newValue) {
pref.setSummary(checkNull(newValue.toString(), pref.getContext()));
return true;
}
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2007 The Android Open Source Project * Copyright (C) 2009 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -59,10 +59,6 @@ class Util {
createErrorDialog(c, message, listener).show(); createErrorDialog(c, message, listener).show();
} }
static boolean isNullOrEmpty(String message) {
return ((message == null) || (message.length() == 0));
}
static String base64Encode(byte[] bytes) { static String base64Encode(byte[] bytes) {
return new String(Base64.encodeBase64(bytes)); return new String(Base64.encodeBase64(bytes));
} }
@@ -132,7 +128,8 @@ class Util {
private static AlertDialog createErrorDialog(Context c, String message, private static AlertDialog createErrorDialog(Context c, String message,
DialogInterface.OnClickListener okListener) { DialogInterface.OnClickListener okListener) {
AlertDialog.Builder b = new AlertDialog.Builder(c) AlertDialog.Builder b = new AlertDialog.Builder(c)
.setTitle(R.string.vpn_error_title) .setTitle(android.R.string.dialog_alert_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(message); .setMessage(message);
if (okListener != null) { if (okListener != null) {
b.setPositiveButton(R.string.vpn_back_button, okListener); b.setPositiveButton(R.string.vpn_back_button, okListener);

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2007 The Android Open Source Project * Copyright (C) 2009 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -22,60 +22,59 @@ import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.net.vpn.L2tpIpsecProfile; import android.net.vpn.L2tpIpsecProfile;
import android.net.vpn.L2tpProfile;
import android.net.vpn.VpnProfile; import android.net.vpn.VpnProfile;
import android.net.vpn.VpnType; import android.net.vpn.VpnType;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable; import android.os.Parcelable;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceActivity; import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup; import android.preference.PreferenceGroup;
import android.text.TextUtils;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View;
/** /**
* The activity class for editing a new or existing VPN profile. * The activity class for editing a new or existing VPN profile.
*/ */
public class VpnEditor extends PreferenceActivity { public class VpnEditor extends PreferenceActivity {
private static final String TAG = VpnEditor.class.getSimpleName();
private static final int MENU_SAVE = Menu.FIRST; private static final int MENU_SAVE = Menu.FIRST;
private static final int MENU_CANCEL = Menu.FIRST + 1; private static final int MENU_CANCEL = Menu.FIRST + 1;
private static final String KEY_PROFILE = "profile";
private EditTextPreference mName;
private VpnProfileEditor mProfileEditor; private VpnProfileEditor mProfileEditor;
private boolean mAddingProfile;
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
VpnProfile p = (VpnProfile) ((savedInstanceState == null)
? getIntent().getParcelableExtra(VpnSettings.KEY_VPN_PROFILE)
: savedInstanceState.getParcelable(KEY_PROFILE));
mProfileEditor = getEditor(p);
mAddingProfile = TextUtils.isEmpty(p.getName());
// Loads the XML preferences file // Loads the XML preferences file
addPreferencesFromResource(R.xml.vpn_edit); addPreferencesFromResource(R.xml.vpn_edit);
mName = (EditTextPreference) findPreference("vpn_name"); initViewFor(p);
mName.setOnPreferenceChangeListener( }
new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(
Preference pref, Object newValue) {
setName((String) newValue);
return true;
}
});
if (savedInstanceState == null) { @Override
VpnProfile p = getIntent().getParcelableExtra( protected synchronized void onSaveInstanceState(Bundle outState) {
VpnSettings.KEY_VPN_PROFILE); if (mProfileEditor == null) return;
initViewFor(p);
} outState.putParcelable(KEY_PROFILE, getProfile());
} }
@Override @Override
public boolean onCreateOptionsMenu(Menu menu) { public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu); super.onCreateOptionsMenu(menu);
menu.add(0, MENU_SAVE, 0, R.string.vpn_menu_save) menu.add(0, MENU_SAVE, 0, R.string.vpn_menu_done)
.setIcon(android.R.drawable.ic_menu_save); .setIcon(android.R.drawable.ic_menu_save);
menu.add(0, MENU_CANCEL, 0, R.string.vpn_menu_cancel) menu.add(0, MENU_CANCEL, 0,
mAddingProfile ? R.string.vpn_menu_cancel
: R.string.vpn_menu_revert)
.setIcon(android.R.drawable.ic_menu_close_clear_cancel); .setIcon(android.R.drawable.ic_menu_close_clear_cancel);
return true; return true;
} }
@@ -96,33 +95,16 @@ public class VpnEditor extends PreferenceActivity {
} }
private void initViewFor(VpnProfile profile) { private void initViewFor(VpnProfile profile) {
VpnProfileEditor editor = getEditor(profile);
VpnType type = profile.getType();
PreferenceGroup subsettings = getPreferenceScreen();
setTitle(profile); setTitle(profile);
setName(profile.getName()); mProfileEditor.loadPreferencesTo(getPreferenceScreen());
editor.loadPreferencesTo(subsettings);
mProfileEditor = editor;
} }
private void setTitle(VpnProfile profile) { private void setTitle(VpnProfile profile) {
if (Util.isNullOrEmpty(profile.getName())) { String formatString = mAddingProfile
setTitle(String.format(getString(R.string.vpn_edit_title_add), ? getString(R.string.vpn_edit_title_add)
profile.getType().getDisplayName())); : getString(R.string.vpn_edit_title_edit);
} else { setTitle(String.format(formatString,
setTitle(String.format(getString(R.string.vpn_edit_title_edit), profile.getType().getDisplayName()));
profile.getType().getDisplayName()));
}
}
private void setName(String newName) {
newName = (newName == null) ? "" : newName.trim();
mName.setText(newName);
mName.setSummary(Util.isNullOrEmpty(newName)
? getString(R.string.vpn_name_summary)
: newName);
} }
/** /**
@@ -130,24 +112,18 @@ public class VpnEditor extends PreferenceActivity {
* @return true if the result is successfully set * @return true if the result is successfully set
*/ */
private boolean validateAndSetResult() { private boolean validateAndSetResult() {
String errorMsg = null; String errorMsg = mProfileEditor.validate();
if (Util.isNullOrEmpty(mName.getText())) {
errorMsg = getString(R.string.vpn_error_name_empty);
} else {
errorMsg = mProfileEditor.validate(this);
}
if (errorMsg != null) { if (errorMsg != null) {
Util.showErrorMessage(this, errorMsg); Util.showErrorMessage(this, errorMsg);
return false; return false;
} }
setResult(mProfileEditor.getProfile()); setResult(getProfile());
return true; return true;
} }
private void setResult(VpnProfile p) { private void setResult(VpnProfile p) {
p.setName(mName.getText());
p.setId(Util.base64Encode(p.getName().getBytes())); p.setId(Util.base64Encode(p.getName().getBytes()));
Intent intent = new Intent(this, VpnSettings.class); Intent intent = new Intent(this, VpnSettings.class);
intent.putExtra(VpnSettings.KEY_VPN_PROFILE, (Parcelable) p); intent.putExtra(VpnSettings.KEY_VPN_PROFILE, (Parcelable) p);
@@ -155,17 +131,26 @@ public class VpnEditor extends PreferenceActivity {
} }
private VpnProfileEditor getEditor(VpnProfile p) { private VpnProfileEditor getEditor(VpnProfile p) {
if (p instanceof L2tpIpsecProfile) { switch (p.getType()) {
return new L2tpIpsecEditor((L2tpIpsecProfile) p); case L2TP_IPSEC:
} else { return new L2tpIpsecEditor((L2tpIpsecProfile) p);
return new VpnProfileEditor(p);
case L2TP_IPSEC_PSK:
case L2TP:
return new L2tpEditor((L2tpProfile) p);
default:
return new VpnProfileEditor(p);
} }
} }
private void showCancellationConfirmDialog() { private void showCancellationConfirmDialog() {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.vpn_error_title) .setTitle(android.R.string.dialog_alert_title)
.setMessage(R.string.vpn_confirm_profile_cancellation) .setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(mAddingProfile
? R.string.vpn_confirm_add_profile_cancellation
: R.string.vpn_confirm_edit_profile_cancellation)
.setPositiveButton(R.string.vpn_yes_button, .setPositiveButton(R.string.vpn_yes_button,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int w) { public void onClick(DialogInterface dialog, int w) {
@@ -175,4 +160,8 @@ public class VpnEditor extends PreferenceActivity {
.setNegativeButton(R.string.vpn_mistake_button, null) .setNegativeButton(R.string.vpn_mistake_button, null)
.show(); .show();
} }
private VpnProfile getProfile() {
return mProfileEditor.getProfile();
}
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2007 The Android Open Source Project * Copyright (C) 2009 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -20,14 +20,21 @@ import com.android.settings.R;
import android.content.Context; import android.content.Context;
import android.net.vpn.VpnProfile; import android.net.vpn.VpnProfile;
import android.preference.CheckBoxPreference;
import android.preference.EditTextPreference; import android.preference.EditTextPreference;
import android.preference.ListPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceGroup; import android.preference.PreferenceGroup;
import android.text.TextUtils;
import android.text.method.PasswordTransformationMethod;
/** /**
* The common class for editing {@link VpnProfile}. * The common class for editing {@link VpnProfile}.
*/ */
class VpnProfileEditor { class VpnProfileEditor {
private static final String KEY_VPN_NAME = "vpn_name";
private EditTextPreference mName;
private EditTextPreference mServerName; private EditTextPreference mServerName;
private EditTextPreference mDomainSuffices; private EditTextPreference mDomainSuffices;
private VpnProfile mProfile; private VpnProfile mProfile;
@@ -47,6 +54,18 @@ class VpnProfileEditor {
*/ */
public void loadPreferencesTo(PreferenceGroup subpanel) { public void loadPreferencesTo(PreferenceGroup subpanel) {
Context c = subpanel.getContext(); Context c = subpanel.getContext();
mName = (EditTextPreference) subpanel.findPreference(KEY_VPN_NAME);
mName.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange(
Preference pref, Object newValue) {
setName((String) newValue);
return true;
}
});
setName(getProfile().getName());
subpanel.addPreference(createServerNamePreference(c)); subpanel.addPreference(createServerNamePreference(c));
loadExtraPreferencesTo(subpanel); loadExtraPreferencesTo(subpanel);
subpanel.addPreference(createDomainSufficesPreference(c)); subpanel.addPreference(createDomainSufficesPreference(c));
@@ -65,60 +84,135 @@ class VpnProfileEditor {
* @return an error message that is ready to be displayed in a dialog; or * @return an error message that is ready to be displayed in a dialog; or
* null if all the inputs are valid * null if all the inputs are valid
*/ */
public String validate(Context c) { public String validate() {
return (Util.isNullOrEmpty(mServerName.getText()) String result = validate(mName, R.string.vpn_name);
? c.getString(R.string.vpn_error_server_name_empty) return ((result != null)
: null); ? result
: validate(mServerName, R.string.vpn_vpn_server));
}
/**
* Saves the secrets in this profile.
* @param originalProfileName the original profile name
*/
public void saveSecrets(String originalProfileName) {
} }
/** /**
* Creates a preference for users to input domain suffices. * Creates a preference for users to input domain suffices.
*/ */
protected EditTextPreference createDomainSufficesPreference(Context c) { protected EditTextPreference createDomainSufficesPreference(Context c) {
EditTextPreference pref = mDomainSuffices = new EditTextPreference(c); mDomainSuffices = createEditTextPreference(c,
pref.setTitle(R.string.vpn_dns_search_list_title); R.string.vpn_dns_search_list_title,
pref.setDialogTitle(R.string.vpn_dns_search_list_title); R.string.vpn_dns_search_list,
pref.setPersistent(true); mProfile.getDomainSuffices(),
pref.setText(mProfile.getDomainSuffices());
pref.setSummary(mProfile.getDomainSuffices());
pref.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() { new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange( public boolean onPreferenceChange(
Preference pref, Object newValue) { Preference pref, Object newValue) {
String v = ((String) newValue).trim(); String v = ((String) newValue).trim();
mProfile.setDomainSuffices(v); mProfile.setDomainSuffices(v);
pref.setSummary(checkNull(v, pref.getContext())); setSummary(pref, R.string.vpn_dns_search_list, v, false);
return true; return true;
} }
}); });
return pref; return mDomainSuffices;
} }
private Preference createServerNamePreference(Context c) { private Preference createServerNamePreference(Context c) {
EditTextPreference serverName = mServerName = new EditTextPreference(c); mServerName = createEditTextPreference(c,
String title = c.getString(R.string.vpn_server_name_title); R.string.vpn_vpn_server_title,
serverName.setTitle(title); R.string.vpn_vpn_server,
serverName.setDialogTitle(title); mProfile.getServerName(),
serverName.setSummary(checkNull(mProfile.getServerName(), c));
serverName.setText(mProfile.getServerName());
serverName.setPersistent(true);
serverName.setOnPreferenceChangeListener(
new Preference.OnPreferenceChangeListener() { new Preference.OnPreferenceChangeListener() {
public boolean onPreferenceChange( public boolean onPreferenceChange(
Preference pref, Object newValue) { Preference pref, Object newValue) {
String v = ((String) newValue).trim(); String v = ((String) newValue).trim();
mProfile.setServerName(v); mProfile.setServerName(v);
pref.setSummary(checkNull(v, pref.getContext())); setSummary(pref, R.string.vpn_vpn_server, v);
return true; return true;
} }
}); });
return mServerName; return mServerName;
} }
protected EditTextPreference createEditTextPreference(Context c, int titleId,
int prefNameId, String value,
Preference.OnPreferenceChangeListener listener) {
EditTextPreference pref = new EditTextPreference(c);
pref.setTitle(titleId);
pref.setDialogTitle(titleId);
setSummary(pref, prefNameId, value);
pref.setText(value);
pref.setPersistent(true);
pref.setOnPreferenceChangeListener(listener);
return pref;
}
String checkNull(String value, Context c) { protected EditTextPreference createSecretPreference(Context c, int titleId,
return ((value != null && value.length() > 0) int fieldNameId, String value,
? value Preference.OnPreferenceChangeListener listener) {
: c.getString(R.string.vpn_not_set)); EditTextPreference pref = new EditTextPreference(c);
} pref.setTitle(titleId);
pref.setDialogTitle(titleId);
pref.getEditText().setTransformationMethod(
new PasswordTransformationMethod());
pref.setText(value);
setSecretSummary(pref, fieldNameId, value);
pref.setPersistent(true);
pref.setOnPreferenceChangeListener(listener);
return pref;
}
protected String validate(Preference pref, int fieldNameId) {
Context c = pref.getContext();
String value = (pref instanceof EditTextPreference)
? ((EditTextPreference) pref).getText()
: ((ListPreference) pref).getValue();
String formatString = (pref instanceof EditTextPreference)
? c.getString(R.string.vpn_error_miss_entering)
: c.getString(R.string.vpn_error_miss_selecting);
return (TextUtils.isEmpty(value)
? String.format(formatString, c.getString(fieldNameId))
: null);
}
protected void setSummary(Preference pref, int fieldNameId, String v) {
setSummary(pref, fieldNameId, v, true);
}
protected void setSummary(Preference pref, int fieldNameId, String v,
boolean required) {
Context c = pref.getContext();
String formatString = required
? c.getString(R.string.vpn_field_not_set)
: c.getString(R.string.vpn_field_not_set_optional);
pref.setSummary(TextUtils.isEmpty(v)
? String.format(formatString, c.getString(fieldNameId))
: v);
}
protected void setSecretSummary(Preference pref, int fieldNameId,
String value) {
Context c = pref.getContext();
String formatString = TextUtils.isEmpty(value)
? c.getString(R.string.vpn_field_not_set)
: c.getString(R.string.vpn_field_is_set);
pref.setSummary(String.format(formatString, c.getString(fieldNameId)));
}
protected void setSecretTitle(
CheckBoxPreference pref, int fieldNameId, boolean enabled) {
Context c = pref.getContext();
String formatString = enabled
? c.getString(R.string.vpn_disable_field)
: c.getString(R.string.vpn_enable_field);
pref.setTitle(String.format(formatString, c.getString(fieldNameId)));
}
private void setName(String newName) {
newName = (newName == null) ? "" : newName.trim();
mName.setText(newName);
getProfile().setName(newName);
setSummary(mName, R.string.vpn_name, newName);
}
} }

View File

@@ -29,6 +29,7 @@ import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState; import android.net.vpn.VpnState;
import android.net.vpn.VpnType; import android.net.vpn.VpnType;
import android.os.Bundle; import android.os.Bundle;
import android.os.ConditionVariable;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.preference.Preference; import android.preference.Preference;
@@ -37,6 +38,7 @@ import android.preference.PreferenceCategory;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.preference.PreferenceScreen; import android.preference.PreferenceScreen;
import android.preference.Preference.OnPreferenceClickListener; import android.preference.Preference.OnPreferenceClickListener;
import android.text.TextUtils;
import android.util.Log; import android.util.Log;
import android.view.ContextMenu; import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo; import android.view.ContextMenu.ContextMenuInfo;
@@ -53,9 +55,13 @@ import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap; import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set;
/** /**
* The preference activity for configuring VPN settings. * The preference activity for configuring VPN settings.
@@ -87,6 +93,9 @@ public class VpnSettings extends PreferenceActivity implements
private static final int CONTEXT_MENU_DELETE_ID = ContextMenu.FIRST + 3; private static final int CONTEXT_MENU_DELETE_ID = ContextMenu.FIRST + 3;
private static final int CONNECT_BUTTON = DialogInterface.BUTTON1; private static final int CONNECT_BUTTON = DialogInterface.BUTTON1;
private static final int OK_BUTTON = DialogInterface.BUTTON1;
private static final int DIALOG_CONNECT = 0;
private PreferenceScreen mAddVpn; private PreferenceScreen mAddVpn;
private PreferenceCategory mVpnListContainer; private PreferenceCategory mVpnListContainer;
@@ -102,6 +111,7 @@ public class VpnSettings extends PreferenceActivity implements
// actor engaged in connecting // actor engaged in connecting
private VpnProfileActor mConnectingActor; private VpnProfileActor mConnectingActor;
private boolean mStateSaved = false;
private VpnManager mVpnManager = new VpnManager(this); private VpnManager mVpnManager = new VpnManager(this);
@@ -110,6 +120,8 @@ public class VpnSettings extends PreferenceActivity implements
private boolean mConnectingError; private boolean mConnectingError;
private StatusChecker mStatusChecker = new StatusChecker();
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@@ -133,16 +145,30 @@ public class VpnSettings extends PreferenceActivity implements
// listen to vpn connectivity event // listen to vpn connectivity event
mVpnManager.registerConnectivityReceiver(mConnectivityReceiver); mVpnManager.registerConnectivityReceiver(mConnectivityReceiver);
String profileName = (savedInstanceState == null)
? null
: savedInstanceState.getString(STATE_ACTIVE_ACTOR);
mStateSaved = !TextUtils.isEmpty(profileName);
retrieveVpnListFromStorage();
if (mStateSaved) {
mConnectingActor =
getActor(mVpnPreferenceMap.get(profileName).mProfile);
} else {
checkVpnConnectionStatusInBackground();
}
}
@Override
public void onPause() {
super.onPause();
mStatusChecker.onPause();
} }
@Override @Override
public void onResume() { public void onResume() {
super.onResume(); super.onResume();
mStatusChecker.onResume();
if ((mVpnProfileList == null) || mVpnProfileList.isEmpty()) {
retrieveVpnListFromStorage();
checkVpnConnectionStatusInBackground();
}
} }
@Override @Override
@@ -153,17 +179,6 @@ public class VpnSettings extends PreferenceActivity implements
mConnectingActor.getProfile().getName()); mConnectingActor.getProfile().getName());
} }
@Override
protected void onRestoreInstanceState(final Bundle savedState) {
String profileName = savedState.getString(STATE_ACTIVE_ACTOR);
if (Util.isNullOrEmpty(profileName)) return;
retrieveVpnListFromStorage();
VpnProfile p = mVpnPreferenceMap.get(profileName).mProfile;
mConnectingActor = getActor(p);
}
@Override @Override
protected void onDestroy() { protected void onDestroy() {
super.onDestroy(); super.onDestroy();
@@ -173,8 +188,19 @@ public class VpnSettings extends PreferenceActivity implements
@Override @Override
protected Dialog onCreateDialog (int id) { protected Dialog onCreateDialog (int id) {
switch (id) {
case DIALOG_CONNECT:
return createConnectDialog();
default:
return null;
}
}
private Dialog createConnectDialog() {
if (mConnectingActor == null) { if (mConnectingActor == null) {
Log.e(TAG, "no connecting actor to create the dialog"); Log.e(TAG, "no connecting actor to create the dialog");
return null;
} }
String name = (mConnectingActor == null) String name = (mConnectingActor == null)
? getString(R.string.vpn_default_profile_name) ? getString(R.string.vpn_default_profile_name)
@@ -185,7 +211,7 @@ public class VpnSettings extends PreferenceActivity implements
name)) name))
.setPositiveButton(getString(R.string.vpn_connect_button), .setPositiveButton(getString(R.string.vpn_connect_button),
this) this)
.setNegativeButton(getString(R.string.vpn_cancel_button), .setNegativeButton(getString(android.R.string.cancel),
this) this)
.create(); .create();
return d; return d;
@@ -193,7 +219,10 @@ public class VpnSettings extends PreferenceActivity implements
@Override @Override
protected void onPrepareDialog (int id, Dialog dialog) { protected void onPrepareDialog (int id, Dialog dialog) {
if (mConnectingActor != null) { if (mStateSaved) {
mStateSaved = false;
super.onPrepareDialog(id, dialog);
} else if (mConnectingActor != null) {
mConnectingActor.updateConnectView(dialog); mConnectingActor.updateConnectView(dialog);
} }
} }
@@ -214,7 +243,8 @@ public class VpnSettings extends PreferenceActivity implements
(isIdle || (state == VpnState.DISCONNECTING)); (isIdle || (state == VpnState.DISCONNECTING));
menu.add(0, CONTEXT_MENU_CONNECT_ID, 0, R.string.vpn_menu_connect) menu.add(0, CONTEXT_MENU_CONNECT_ID, 0, R.string.vpn_menu_connect)
.setEnabled(isIdle && (mActiveProfile == null)); .setEnabled(isIdle && (mActiveProfile == null));
menu.add(0, CONTEXT_MENU_DISCONNECT_ID, 0, R.string.vpn_menu_disconnect) menu.add(0, CONTEXT_MENU_DISCONNECT_ID, 0,
R.string.vpn_menu_disconnect)
.setEnabled(state == VpnState.CONNECTED); .setEnabled(state == VpnState.CONNECTED);
menu.add(0, CONTEXT_MENU_EDIT_ID, 0, R.string.vpn_menu_edit) menu.add(0, CONTEXT_MENU_EDIT_ID, 0, R.string.vpn_menu_edit)
.setEnabled(isNotConnect); .setEnabled(isNotConnect);
@@ -272,7 +302,8 @@ public class VpnSettings extends PreferenceActivity implements
if (checkDuplicateName(p, index)) { if (checkDuplicateName(p, index)) {
final VpnProfile profile = p; final VpnProfile profile = p;
Util.showErrorMessage(this, String.format( Util.showErrorMessage(this, String.format(
getString(R.string.vpn_error_duplicate_name), p.getName()), getString(R.string.vpn_error_duplicate_name),
p.getName()),
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int w) { public void onClick(DialogInterface dialog, int w) {
startVpnEditor(profile); startVpnEditor(profile);
@@ -289,7 +320,8 @@ public class VpnSettings extends PreferenceActivity implements
} else { } else {
replaceProfile(index, p); replaceProfile(index, p);
Util.showShortToastMessage(this, String.format( Util.showShortToastMessage(this, String.format(
getString(R.string.vpn_profile_replaced), p.getName())); getString(R.string.vpn_profile_replaced),
p.getName()));
} }
} catch (IOException e) { } catch (IOException e) {
final VpnProfile profile = p; final VpnProfile profile = p;
@@ -308,7 +340,7 @@ public class VpnSettings extends PreferenceActivity implements
// Called when the buttons on the connect dialog are clicked. // Called when the buttons on the connect dialog are clicked.
//@Override //@Override
public synchronized void onClick(DialogInterface dialog, int which) { public synchronized void onClick(DialogInterface dialog, int which) {
dismissDialog(0); dismissDialog(DIALOG_CONNECT);
if (which == CONNECT_BUTTON) { if (which == CONNECT_BUTTON) {
Dialog d = (Dialog) dialog; Dialog d = (Dialog) dialog;
String error = mConnectingActor.validateInputs(d); String error = mConnectingActor.validateInputs(d);
@@ -319,14 +351,15 @@ public class VpnSettings extends PreferenceActivity implements
} else { } else {
// show error dialog // show error dialog
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.vpn_you_miss_a_field) .setTitle(android.R.string.dialog_alert_title)
.setMessage(String.format( .setIcon(android.R.drawable.ic_dialog_alert)
getString(R.string.vpn_please_fill_up), error)) .setMessage(String.format(getString(
R.string.vpn_error_miss_entering), error))
.setPositiveButton(R.string.vpn_back_button, .setPositiveButton(R.string.vpn_back_button,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, public void onClick(DialogInterface dialog,
int which) { int which) {
showDialog(0); showDialog(DIALOG_CONNECT);
} }
}) })
.show(); .show();
@@ -357,12 +390,27 @@ public class VpnSettings extends PreferenceActivity implements
} }
// position: position in mVpnProfileList // position: position in mVpnProfileList
private void deleteProfile(int position) { private void deleteProfile(final int position) {
if ((position < 0) || (position >= mVpnProfileList.size())) return; if ((position < 0) || (position >= mVpnProfileList.size())) return;
VpnProfile p = mVpnProfileList.remove(position); DialogInterface.OnClickListener onClickListener =
VpnPreference pref = mVpnPreferenceMap.remove(p.getName()); new DialogInterface.OnClickListener() {
mVpnListContainer.removePreference(pref); public void onClick(DialogInterface dialog, int which) {
removeProfileFromStorage(p); dialog.dismiss();
if (which == OK_BUTTON) {
VpnProfile p = mVpnProfileList.remove(position);
VpnPreference pref =
mVpnPreferenceMap.remove(p.getName());
mVpnListContainer.removePreference(pref);
removeProfileFromStorage(p);
}
}
};
new AlertDialog.Builder(this)
.setTitle(android.R.string.dialog_alert_title)
.setMessage(R.string.vpn_confirm_profile_deletion)
.setPositiveButton(android.R.string.ok, onClickListener)
.setNegativeButton(R.string.vpn_no_button, onClickListener)
.show();
} }
private void addProfile(VpnProfile p) throws IOException { private void addProfile(VpnProfile p) throws IOException {
@@ -396,6 +444,8 @@ public class VpnSettings extends PreferenceActivity implements
throw new RuntimeException("inconsistent state!"); throw new RuntimeException("inconsistent state!");
} }
// TODO: call saveSecret(String) after keystore is available
// Copy config files and remove the old ones if they are in different // Copy config files and remove the old ones if they are in different
// directories. // directories.
if (Util.copyFiles(getProfileDir(oldProfile), getProfileDir(p))) { if (Util.copyFiles(getProfileDir(oldProfile), getProfileDir(p))) {
@@ -423,9 +473,10 @@ public class VpnSettings extends PreferenceActivity implements
VpnPreference pref = mVpnPreferenceMap.get(p.getName()); VpnPreference pref = mVpnPreferenceMap.get(p.getName());
switch (p.getState()) { switch (p.getState()) {
case IDLE: case IDLE:
mConnectingActor = getActor(new VpnProfileWrapper(p)); mConnectingActor = getActor(p);
if (mConnectingActor.isConnectDialogNeeded()) { if (mConnectingActor.isConnectDialogNeeded()) {
showDialog(0); removeDialog(DIALOG_CONNECT);
showDialog(DIALOG_CONNECT);
} else { } else {
changeState(p, VpnState.CONNECTING); changeState(p, VpnState.CONNECTING);
mConnectingActor.connect(null); mConnectingActor.connect(null);
@@ -487,7 +538,7 @@ public class VpnSettings extends PreferenceActivity implements
private void showReconnectDialog(final VpnProfile p) { private void showReconnectDialog(final VpnProfile p) {
new AlertDialog.Builder(this) new AlertDialog.Builder(this)
.setTitle(R.string.vpn_error_title) .setTitle(android.R.string.dialog_alert_title)
.setMessage(R.string.vpn_confirm_reconnect) .setMessage(R.string.vpn_confirm_reconnect)
.setPositiveButton(R.string.vpn_yes_button, .setPositiveButton(R.string.vpn_yes_button,
new DialogInterface.OnClickListener() { new DialogInterface.OnClickListener() {
@@ -522,11 +573,11 @@ public class VpnSettings extends PreferenceActivity implements
} }
} }
private String getProfileDir(VpnProfile p) { static String getProfileDir(VpnProfile p) {
return PROFILES_ROOT + p.getId(); return PROFILES_ROOT + p.getId();
} }
private void saveProfileToStorage(VpnProfile p) throws IOException { static void saveProfileToStorage(VpnProfile p) throws IOException {
File f = new File(getProfileDir(p)); File f = new File(getProfileDir(p));
if (!f.exists()) f.mkdirs(); if (!f.exists()) f.mkdirs();
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream( ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
@@ -541,7 +592,8 @@ public class VpnSettings extends PreferenceActivity implements
private void retrieveVpnListFromStorage() { private void retrieveVpnListFromStorage() {
mVpnPreferenceMap = new LinkedHashMap<String, VpnPreference>(); mVpnPreferenceMap = new LinkedHashMap<String, VpnPreference>();
mVpnProfileList = new ArrayList<VpnProfile>(); mVpnProfileList = Collections.synchronizedList(
new ArrayList<VpnProfile>());
mVpnListContainer.removeAll(); mVpnListContainer.removeAll();
File root = new File(PROFILES_ROOT); File root = new File(PROFILES_ROOT);
@@ -566,13 +618,7 @@ public class VpnSettings extends PreferenceActivity implements
} }
private void checkVpnConnectionStatusInBackground() { private void checkVpnConnectionStatusInBackground() {
new Thread(new Runnable() { mStatusChecker.check(mVpnProfileList);
public void run() {
for (VpnProfile p : mVpnProfileList) {
getActor(p).checkStatus();
}
}
}).start();
} }
// A sanity check. Returns true if the profile directory name and profile ID // A sanity check. Returns true if the profile directory name and profile ID
@@ -661,99 +707,50 @@ public class VpnSettings extends PreferenceActivity implements
} }
} }
// to catch saved user name in the connect dialog // managing status check in a background thread
private class VpnProfileWrapper extends VpnProfile { private class StatusChecker {
private VpnProfile mProfile; private Set<VpnProfile> mQueue = new HashSet<VpnProfile>();
private boolean mPaused;
private ConditionVariable mThreadCv = new ConditionVariable();
VpnProfileWrapper(VpnProfile p) { void onPause() {
mProfile = p; mPaused = true;
mThreadCv.block(); // until the checking thread is over
} }
@Override synchronized void onResume() {
public void setSavedUsername(String name) { mPaused = false;
if ((name != null) && !name.equals(mProfile.getSavedUsername())) { start();
mProfile.setSavedUsername(name); }
try {
saveProfileToStorage(mProfile); synchronized void check(List<VpnProfile> list) {
} catch (IOException e) { boolean started = !mQueue.isEmpty();
Log.d(TAG, "save username", e); for (VpnProfile p : list) {
// harmless if (!mQueue.contains(p)) mQueue.add(p);
}
} }
if (!started) start();
} }
@Override private synchronized VpnProfile next() {
public String getSavedUsername() { if (mPaused || mQueue.isEmpty()) return null;
return mProfile.getSavedUsername(); Iterator<VpnProfile> i = mQueue.iterator();
VpnProfile p = i.next();
i.remove();
return p;
} }
@Override private void start() {
public void writeToParcel(Parcel parcel, int flags) { mThreadCv.close();
mProfile.writeToParcel(parcel, flags); new Thread(new Runnable() {
} public void run() {
while (true) {
@Override VpnProfile p = next();
public void setName(String name) { if (p == null) break;
} getActor(p).checkStatus();
}
@Override mThreadCv.open();
public String getName() { }
return mProfile.getName(); }).start();
}
@Override
public void setId(String id) {
}
@Override
public String getId() {
return mProfile.getId();
}
@Override
public void setServerName(String name) {
}
@Override
public String getServerName() {
return mProfile.getServerName();
}
@Override
public void setDomainSuffices(String entries) {
}
@Override
public String getDomainSuffices() {
return mProfile.getDomainSuffices();
}
@Override
public void setRouteList(String entries) {
}
@Override
public String getRouteList() {
return mProfile.getRouteList();
}
@Override
public void setState(VpnState state) {
}
@Override
public VpnState getState() {
return mProfile.getState();
}
@Override
public boolean isIdle() {
return mProfile.isIdle();
}
@Override
public VpnType getType() {
return mProfile.getType();
} }
} }
} }

View File

@@ -1,5 +1,5 @@
/* /*
* Copyright (C) 2007 The Android Open Source Project * Copyright (C) 2009 The Android Open Source Project
* *
* Licensed under the Apache License, Version 2.0 (the "License"); * Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License. * you may not use this file except in compliance with the License.
@@ -54,10 +54,13 @@ public class VpnTypeSelection extends PreferenceActivity {
PreferenceScreen root = getPreferenceScreen(); PreferenceScreen root = getPreferenceScreen();
for (VpnType t : VpnManager.getSupportedVpnTypes()) { for (VpnType t : VpnManager.getSupportedVpnTypes()) {
String displayName = t.getDisplayName(); String displayName = t.getDisplayName();
mTypeMap.put(displayName, t); String message = String.format(
getString(R.string.vpn_edit_title_add), displayName);
mTypeMap.put(message, t);
Preference pref = new Preference(this); Preference pref = new Preference(this);
pref.setTitle(displayName); pref.setTitle(message);
pref.setSummary(t.getDescription());
root.addPreference(pref); root.addPreference(pref);
} }
} }