Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d22da209 authored by Hung-ying Tyan's avatar Hung-ying Tyan
Browse files

Add save-username checkbox in the connect dialog.

* changes
  + Add checkbox to layout file and handling code in related classes.
  + Add new methods to VpnProfileActor.
  + Move dialog handling to VpnSettings from AuthenticatorActor in order
    to support screen orientation change.
  + Hide "Connect" in the context menu if the profile is connecting.
  + Enable connecting profile in case it was disabled in last call.
parent 4226b0af
Loading
Loading
Loading
Loading
+6 −0
Original line number Original line Diff line number Diff line
@@ -45,4 +45,10 @@
                android:singleLine="True"/>
                android:singleLine="True"/>
    </LinearLayout>
    </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>
</LinearLayout>
+4 −2
Original line number Original line Diff line number Diff line
@@ -1755,6 +1755,7 @@ found in the list of installed applications.</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">User name</string>
    <string name="vpn_password">Password</string>
    <string name="vpn_password">Password</string>
    <string name="vpn_save_username">Remember me</string>
    <string name="vpn_you_miss_a_field">You missed a field!</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_please_fill_up">Please fill up \"%s\".</string>


@@ -1800,10 +1801,11 @@ found in the list of installed applications.</string>
    <!-- EditTextPreference summary text when VPN is not connected -->
    <!-- EditTextPreference summary text when VPN is not connected -->
    <string name="vpn_connect_hint">Select to connect</string>
    <string name="vpn_connect_hint">Select to connect</string>
    <!-- dialog title when asking for username and password -->
    <!-- dialog title when asking for username and password -->
    <string name="vpn_connect_to">Connect to</string>
    <string name="vpn_connect_to">Connect to %s</string>
    <string name="vpn_default_profile_name">nowhere</string>


    <string name="vpn_name">VPN Name</string>
    <string name="vpn_name">VPN Name</string>
    <string name="vpn_name_summary">Give a name to this VPN;</string>
    <string name="vpn_name_summary">Give a name to this VPN</string>


    <string name="vpn_profile_added">'%s' is added</string>
    <string name="vpn_profile_added">'%s' is added</string>
    <string name="vpn_profile_replaced">Changes are made to '%s'</string>
    <string name="vpn_profile_replaced">Changes are made to '%s'</string>
+50 −133
Original line number Original line Diff line number Diff line
/*
/*
 * 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.
@@ -18,48 +18,32 @@ package com.android.settings.vpn;


import com.android.settings.R;
import com.android.settings.R;


import android.app.AlertDialog;
import android.app.Dialog;
import android.content.ComponentName;
import android.content.ComponentName;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.ServiceConnection;
import android.net.vpn.IVpnService;
import android.net.vpn.IVpnService;
import android.net.vpn.VpnManager;
import android.net.vpn.VpnManager;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnState;
import android.net.vpn.VpnState;
import android.os.Bundle;
import android.os.IBinder;
import android.os.IBinder;
import android.os.Parcelable;
import android.os.RemoteException;
import android.os.RemoteException;
import android.util.Log;
import android.util.Log;
import android.view.View;
import android.view.View;
import android.widget.EditText;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.TextView;
import android.widget.Toast;

import java.io.IOException;


/**
/**
 * A {@link VpnProfileActor} that provides an authentication view for users to
 * input username and password before connecting to the VPN server.
 */
 */
public class AuthenticationActor implements VpnProfileActor,
public class AuthenticationActor implements VpnProfileActor {
        DialogInterface.OnClickListener, DialogInterface.OnCancelListener {
    private static final String TAG = AuthenticationActor.class.getName();
    private static final String TAG = AuthenticationActor.class.getName();
    private static final int ONE_SECOND = 1000; // ms
    private static final int ONE_SECOND = 1000; // ms


    private static final String STATE_IS_DIALOG_OPEN = "is_dialog_open";
    private static final String STATE_USERNAME = "username";
    private static final String STATE_PASSWORD = "password";

    private Context mContext;
    private Context mContext;
    private TextView mUsernameView;
    private TextView mPasswordView;

    private VpnProfile mProfile;
    private VpnProfile mProfile;
    private View mView;
    private VpnManager mVpnManager;
    private VpnManager mVpnManager;
    private AlertDialog mConnectDialog;
    private AlertDialog mDisconnectDialog;


    public AuthenticationActor(Context context, VpnProfile p) {
    public AuthenticationActor(Context context, VpnProfile p) {
        mContext = context;
        mContext = context;
@@ -73,77 +57,54 @@ public class AuthenticationActor implements VpnProfileActor,
    }
    }


    //@Override
    //@Override
    public synchronized void connect() {
    public boolean isConnectDialogNeeded() {
        connect("", "");
        return true;
    }
    }


    //@Override
    //@Override
    public void onClick(DialogInterface dialog, int which) {
    public String validateInputs(Dialog d) {
        dismissConnectDialog();
        TextView usernameView = (TextView) d.findViewById(R.id.username_value);
        switch (which) {
        TextView passwordView = (TextView) d.findViewById(R.id.password_value);
        case DialogInterface.BUTTON1: // connect
        Context c = mContext;
            if (validateInputs()) {
        if (Util.isNullOrEmpty(usernameView.getText().toString())) {
                broadcastConnectivity(VpnState.CONNECTING);
            return c.getString(R.string.vpn_username);
                connectInternal();
        } else if (Util.isNullOrEmpty(passwordView.getText().toString())) {
            }
            return c.getString(R.string.vpn_password);
            break;
        } else {

            return null;
        case DialogInterface.BUTTON2: // cancel
            broadcastConnectivity(VpnState.CANCELLED);
            break;
        }
        }
    }
    }


    //@Override
    //@Override
    public void onCancel(DialogInterface dialog) {
    public void connect(Dialog d) {
        dismissConnectDialog();
        TextView usernameView = (TextView) d.findViewById(R.id.username_value);
        broadcastConnectivity(VpnState.CANCELLED);
        TextView passwordView = (TextView) d.findViewById(R.id.password_value);
        CheckBox saveUsername = (CheckBox) d.findViewById(R.id.save_username);

        // save username
        if (saveUsername.isChecked()) {
            mProfile.setSavedUsername(usernameView.getText().toString());
        } else {
            mProfile.setSavedUsername("");
        }
        }

        connect(usernameView.getText().toString(),
    private void connect(String username, String password) {
                passwordView.getText().toString());
        Context c = mContext;
        passwordView.setText("");
        mConnectDialog = new AlertDialog.Builder(c)
                .setView(createConnectView(username, password))
                .setTitle(c.getString(R.string.vpn_connect_to) + " "
                        + mProfile.getName())
                .setPositiveButton(c.getString(R.string.vpn_connect_button),
                        this)
                .setNegativeButton(c.getString(R.string.vpn_cancel_button),
                        this)
                .setOnCancelListener(this)
                .create();
        mConnectDialog.show();
    }
    }


    //@Override
    //@Override
    public synchronized void onSaveState(Bundle outState) {
    public View createConnectView() {
        outState.putBoolean(STATE_IS_DIALOG_OPEN, (mConnectDialog != null));
        return View.inflate(mContext, R.layout.vpn_connect_dialog_view, null);
        if (mConnectDialog != null) {
            assert(mConnectDialog.isShowing());
            outState.putBoolean(STATE_IS_DIALOG_OPEN, (mConnectDialog != null));
            outState.putString(STATE_USERNAME,
                    mUsernameView.getText().toString());
            outState.putString(STATE_PASSWORD,
                    mPasswordView.getText().toString());
            dismissConnectDialog();
        }
    }
    }


    //@Override
    //@Override
    public synchronized void onRestoreState(final Bundle savedState) {
    public void updateConnectView(Dialog d) {
        boolean isDialogOpen = savedState.getBoolean(STATE_IS_DIALOG_OPEN);
        String username = mProfile.getSavedUsername();
        if (isDialogOpen) {
        if (username == null) username = "";
            connect(savedState.getString(STATE_USERNAME),
        updateConnectView(d, username, "", !Util.isNullOrEmpty(username));
                    savedState.getString(STATE_PASSWORD));
        }
    }

    private synchronized void dismissConnectDialog() {
        mConnectDialog.dismiss();
        mConnectDialog = null;
    }
    }


    private void connectInternal() {
    private void connect(final String username, final String password) {
        mVpnManager.startVpnService();
        mVpnManager.startVpnService();
        ServiceConnection c = new ServiceConnection() {
        ServiceConnection c = new ServiceConnection() {
            public void onServiceConnected(ComponentName className,
            public void onServiceConnected(ComponentName className,
@@ -151,10 +112,7 @@ public class AuthenticationActor implements VpnProfileActor,
                boolean success = false;
                boolean success = false;
                try {
                try {
                    success = IVpnService.Stub.asInterface(service)
                    success = IVpnService.Stub.asInterface(service)
                            .connect(mProfile,
                            .connect(mProfile, username, password);
                                    mUsernameView.getText().toString(),
                                    mPasswordView.getText().toString());
                    mPasswordView.setText("");
                } catch (Throwable e) {
                } catch (Throwable e) {
                    Log.e(TAG, "connect()", e);
                    Log.e(TAG, "connect()", e);
                    checkStatus();
                    checkStatus();
@@ -230,59 +188,18 @@ public class AuthenticationActor implements VpnProfileActor,
        return mVpnManager.bindVpnService(c);
        return mVpnManager.bindVpnService(c);
    }
    }


    private void broadcastConnectivity(VpnState s) {
    private void updateConnectView(Dialog d, String username,
        mVpnManager.broadcastConnectivity(mProfile.getName(), s);
            String password, boolean toSaveUsername) {
    }
        TextView usernameView = (TextView) d.findViewById(R.id.username_value);

        TextView passwordView = (TextView) d.findViewById(R.id.password_value);
    // returns true if inputs pass validation
        CheckBox saveUsername = (CheckBox) d.findViewById(R.id.save_username);
    private boolean validateInputs() {
        usernameView.setText(username);
        Context c = mContext;
        passwordView.setText(password);
        String error = null;
        saveUsername.setChecked(toSaveUsername);
        if (Util.isNullOrEmpty(mUsernameView.getText().toString())) {
            error = c.getString(R.string.vpn_username);
        } else if (Util.isNullOrEmpty(mPasswordView.getText().toString())) {
            error = c.getString(R.string.vpn_password);
        }
        if (error == null) {
            return true;
        } else {
            new AlertDialog.Builder(c)
                    .setTitle(c.getString(R.string.vpn_you_miss_a_field))
                    .setMessage(String.format(
                            c.getString(R.string.vpn_please_fill_up), error))
                    .setPositiveButton(c.getString(R.string.vpn_back_button),
                            createBackButtonListener())
                    .show();
            return false;
        }
    }

    private View createConnectView(String username, String password) {
        View v = View.inflate(mContext, R.layout.vpn_connect_dialog_view, null);
        mUsernameView = (TextView) v.findViewById(R.id.username_value);
        mPasswordView = (TextView) v.findViewById(R.id.password_value);
        mUsernameView.setText(username);
        mPasswordView.setText(password);
        copyFieldsFromOldView(v);
        mView = v;
        return v;
    }

    private void copyFieldsFromOldView(View newView) {
        if (mView == null) return;
        mUsernameView.setText(
                ((TextView) mView.findViewById(R.id.username_value)).getText());
        mPasswordView.setText(
                ((TextView) mView.findViewById(R.id.password_value)).getText());
    }
    }


    private DialogInterface.OnClickListener createBackButtonListener() {
    private void broadcastConnectivity(VpnState s) {
        return new DialogInterface.OnClickListener() {
        mVpnManager.broadcastConnectivity(mProfile.getName(), s);
            public void onClick(DialogInterface dialog, int which) {
                dialog.dismiss();
                connect();
            }
        };
    }
    }


    private void wait(Object o, int ms) {
    private void wait(Object o, int ms) {
+29 −13
Original line number Original line Diff line number Diff line
/*
/*
 * 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.
@@ -16,8 +16,9 @@


package com.android.settings.vpn;
package com.android.settings.vpn;


import android.app.Dialog;
import android.net.vpn.VpnProfile;
import android.net.vpn.VpnProfile;
import android.os.Bundle;
import android.view.View;


/**
/**
 * The interface to act on a {@link VpnProfile}.
 * The interface to act on a {@link VpnProfile}.
@@ -25,10 +26,35 @@ import android.os.Bundle;
public interface VpnProfileActor {
public interface VpnProfileActor {
    VpnProfile getProfile();
    VpnProfile getProfile();


    /**
     * Returns true if a connect dialog is needed before establishing a
     * connection.
     */
    boolean isConnectDialogNeeded();

    /**
     * Creates the view in the connect dialog.
     */
    View createConnectView();

    /**
     * Updates the view in the connect dialog.
     * @param dialog the recycled connect dialog.
     */
    void updateConnectView(Dialog dialog);

    /**
     * Validates the inputs in the dialog.
     * @param dialog the connect dialog
     * @return an error message if the inputs are not valid
     */
    String validateInputs(Dialog dialog);

    /**
    /**
     * Establishes a VPN connection.
     * Establishes a VPN connection.
     * @param dialog the connect dialog
     */
     */
    void connect();
    void connect(Dialog dialog);


    /**
    /**
     * Tears down the connection.
     * Tears down the connection.
@@ -41,14 +67,4 @@ public interface VpnProfileActor {
     * broadcast receiver and to receives the broadcast events.
     * broadcast receiver and to receives the broadcast events.
     */
     */
    void checkStatus();
    void checkStatus();

    /**
     * Called to save the states when the device is rotated.
     */
    void onSaveState(Bundle outState);

    /**
     * Called to restore the states on the rotated screen.
     */
    void onRestoreState(Bundle savedState);
}
}
+201 −27
Original line number Original line Diff line number Diff line
/*
/*
 * 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.
@@ -19,6 +19,7 @@ package com.android.settings.vpn;
import com.android.settings.R;
import com.android.settings.R;


import android.app.AlertDialog;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.BroadcastReceiver;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface;
@@ -28,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.Parcel;
import android.os.Parcelable;
import android.os.Parcelable;
import android.preference.Preference;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceActivity;
@@ -58,7 +60,8 @@ import java.util.Map;
/**
/**
 * The preference activity for configuring VPN settings.
 * The preference activity for configuring VPN settings.
 */
 */
public class VpnSettings extends PreferenceActivity {
public class VpnSettings extends PreferenceActivity implements
        DialogInterface.OnClickListener {
    // Key to the field exchanged for profile editing.
    // Key to the field exchanged for profile editing.
    static final String KEY_VPN_PROFILE = "vpn_profile";
    static final String KEY_VPN_PROFILE = "vpn_profile";


@@ -83,6 +86,8 @@ public class VpnSettings extends PreferenceActivity {
    private static final int CONTEXT_MENU_EDIT_ID = ContextMenu.FIRST + 2;
    private static final int CONTEXT_MENU_EDIT_ID = ContextMenu.FIRST + 2;
    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 PreferenceScreen mAddVpn;
    private PreferenceScreen mAddVpn;
    private PreferenceCategory mVpnListContainer;
    private PreferenceCategory mVpnListContainer;


@@ -95,8 +100,8 @@ public class VpnSettings extends PreferenceActivity {
    // profile engaged in a connection
    // profile engaged in a connection
    private VpnProfile mActiveProfile;
    private VpnProfile mActiveProfile;


    // actor engaged in an action
    // actor engaged in connecting
    private VpnProfileActor mActiveActor;
    private VpnProfileActor mConnectingActor;


    private VpnManager mVpnManager = new VpnManager(this);
    private VpnManager mVpnManager = new VpnManager(this);


@@ -112,7 +117,6 @@ public class VpnSettings extends PreferenceActivity {


        // restore VpnProfile list and construct VpnPreference map
        // restore VpnProfile list and construct VpnPreference map
        mVpnListContainer = (PreferenceCategory) findPreference(PREF_VPN_LIST);
        mVpnListContainer = (PreferenceCategory) findPreference(PREF_VPN_LIST);
        retrieveVpnListFromStorage();


        // set up the "add vpn" preference
        // set up the "add vpn" preference
        mAddVpn = (PreferenceScreen) findPreference(PREF_ADD_VPN);
        mAddVpn = (PreferenceScreen) findPreference(PREF_ADD_VPN);
@@ -131,13 +135,22 @@ public class VpnSettings extends PreferenceActivity {
        mVpnManager.registerConnectivityReceiver(mConnectivityReceiver);
        mVpnManager.registerConnectivityReceiver(mConnectivityReceiver);
    }
    }


    @Override
    public void onResume() {
        super.onResume();

        if ((mVpnProfileList == null) || mVpnProfileList.isEmpty()) {
            retrieveVpnListFromStorage();
            checkVpnConnectionStatusInBackground();
        }
    }

    @Override
    @Override
    protected synchronized void onSaveInstanceState(Bundle outState) {
    protected synchronized void onSaveInstanceState(Bundle outState) {
        if (mActiveActor == null) return;
        if (mConnectingActor == null) return;


        mActiveActor.onSaveState(outState);
        outState.putString(STATE_ACTIVE_ACTOR,
        outState.putString(STATE_ACTIVE_ACTOR,
                mActiveActor.getProfile().getName());
                mConnectingActor.getProfile().getName());
    }
    }


    @Override
    @Override
@@ -145,9 +158,10 @@ public class VpnSettings extends PreferenceActivity {
        String profileName = savedState.getString(STATE_ACTIVE_ACTOR);
        String profileName = savedState.getString(STATE_ACTIVE_ACTOR);
        if (Util.isNullOrEmpty(profileName)) return;
        if (Util.isNullOrEmpty(profileName)) return;


        final VpnProfile p = mVpnPreferenceMap.get(profileName).mProfile;
        retrieveVpnListFromStorage();
        mActiveActor = getActor(p);

        mActiveActor.onRestoreState(savedState);
        VpnProfile p = mVpnPreferenceMap.get(profileName).mProfile;
        mConnectingActor = getActor(p);
    }
    }


    @Override
    @Override
@@ -157,6 +171,33 @@ public class VpnSettings extends PreferenceActivity {
        mVpnManager.unregisterConnectivityReceiver(mConnectivityReceiver);
        mVpnManager.unregisterConnectivityReceiver(mConnectivityReceiver);
    }
    }


    @Override
    protected Dialog onCreateDialog (int id) {
        if (mConnectingActor == null) {
            Log.e(TAG, "no connecting actor to create the dialog");
        }
        String name = (mConnectingActor == null)
                ? getString(R.string.vpn_default_profile_name)
                : mConnectingActor.getProfile().getName();
        Dialog d = new AlertDialog.Builder(this)
                .setView(mConnectingActor.createConnectView())
                .setTitle(String.format(getString(R.string.vpn_connect_to),
                        name))
                .setPositiveButton(getString(R.string.vpn_connect_button),
                        this)
                .setNegativeButton(getString(R.string.vpn_cancel_button),
                        this)
                .create();
        return d;
    }

    @Override
    protected void onPrepareDialog (int id, Dialog dialog) {
        if (mConnectingActor != null) {
            mConnectingActor.updateConnectView(dialog);
        }
    }

    @Override
    @Override
    public void onCreateContextMenu(ContextMenu menu, View v,
    public void onCreateContextMenu(ContextMenu menu, View v,
            ContextMenuInfo menuInfo) {
            ContextMenuInfo menuInfo) {
@@ -174,7 +215,7 @@ public class VpnSettings extends PreferenceActivity {
            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(!isIdle);
                    .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);
            menu.add(0, CONTEXT_MENU_DELETE_ID, 0, R.string.vpn_menu_delete)
            menu.add(0, CONTEXT_MENU_DELETE_ID, 0, R.string.vpn_menu_delete)
@@ -214,7 +255,7 @@ public class VpnSettings extends PreferenceActivity {
        mIndexOfEditedProfile = -1;
        mIndexOfEditedProfile = -1;


        if ((resultCode == RESULT_CANCELED) || (data == null)) {
        if ((resultCode == RESULT_CANCELED) || (data == null)) {
            Log.v(TAG, "no result returned by editor");
            Log.d(TAG, "no result returned by editor");
            return;
            return;
        }
        }


@@ -264,6 +305,35 @@ public class VpnSettings extends PreferenceActivity {
        }
        }
    }
    }


    // Called when the buttons on the connect dialog are clicked.
    //@Override
    public synchronized void onClick(DialogInterface dialog, int which) {
        dismissDialog(0);
        if (which == CONNECT_BUTTON) {
            Dialog d = (Dialog) dialog;
            String error = mConnectingActor.validateInputs(d);
            if (error == null) {
                changeState(mConnectingActor.getProfile(), VpnState.CONNECTING);
                mConnectingActor.connect(d);
                return;
            } else {
                // show error dialog
                new AlertDialog.Builder(this)
                        .setTitle(R.string.vpn_you_miss_a_field)
                        .setMessage(String.format(
                                getString(R.string.vpn_please_fill_up), error))
                        .setPositiveButton(R.string.vpn_back_button,
                                new DialogInterface.OnClickListener() {
                                    public void onClick(DialogInterface dialog,
                                            int which) {
                                        showDialog(0);
                                    }
                                })
                        .show();
            }
        }
    }

    // Replaces the profile at index in mVpnProfileList with p.
    // Replaces the profile at index in mVpnProfileList with p.
    // Returns true if p's name is a duplicate.
    // Returns true if p's name is a duplicate.
    private boolean checkDuplicateName(VpnProfile p, int index) {
    private boolean checkDuplicateName(VpnProfile p, int index) {
@@ -353,13 +423,17 @@ public class VpnSettings extends PreferenceActivity {
        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));
                if (mConnectingActor.isConnectDialogNeeded()) {
                    showDialog(0);
                } else {
                    changeState(p, VpnState.CONNECTING);
                    changeState(p, VpnState.CONNECTING);
                mActiveActor = getActor(p);
                    mConnectingActor.connect(null);
                mActiveActor.connect();
                }
                break;
                break;


            case CONNECTING:
            case CONNECTING:
                // TODO: bring up a dialog to confirm disconnect
                // do nothing
                break;
                break;


            case CONNECTED:
            case CONNECTED:
@@ -376,14 +450,13 @@ public class VpnSettings extends PreferenceActivity {
        VpnState oldState = p.getState();
        VpnState oldState = p.getState();
        if (oldState == state) return;
        if (oldState == state) return;


        Log.d(TAG, "changeState: " + p.getName() + ": " + state);
        p.setState(state);
        p.setState(state);
        mVpnPreferenceMap.get(p.getName()).setSummary(
        mVpnPreferenceMap.get(p.getName()).setSummary(
                getProfileSummaryString(p));
                getProfileSummaryString(p));


        switch (state) {
        switch (state) {
        case CONNECTED:
        case CONNECTED:
            mActiveActor = null;
            mConnectingActor = null;
            // pass through
            // pass through
        case CONNECTING:
        case CONNECTING:
            mActiveProfile = p;
            mActiveProfile = p;
@@ -403,7 +476,7 @@ public class VpnSettings extends PreferenceActivity {
        case IDLE:
        case IDLE:
            assert(mActiveProfile != p);
            assert(mActiveProfile != p);
            mActiveProfile = null;
            mActiveProfile = null;
            mActiveActor = null;
            mConnectingActor = null;
            enableProfilePreferences();
            enableProfilePreferences();


            if (oldState == VpnState.CONNECTING) mConnectingError = true;
            if (oldState == VpnState.CONNECTING) mConnectingError = true;
@@ -436,6 +509,9 @@ public class VpnSettings extends PreferenceActivity {
                case IDLE:
                case IDLE:
                    mVpnPreferenceMap.get(p.getName()).setEnabled(false);
                    mVpnPreferenceMap.get(p.getName()).setEnabled(false);
                    break;
                    break;

                default:
                    mVpnPreferenceMap.get(p.getName()).setEnabled(true);
            }
            }
        }
        }
    }
    }
@@ -466,6 +542,7 @@ public class VpnSettings extends PreferenceActivity {
    private void retrieveVpnListFromStorage() {
    private void retrieveVpnListFromStorage() {
        mVpnPreferenceMap = new LinkedHashMap<String, VpnPreference>();
        mVpnPreferenceMap = new LinkedHashMap<String, VpnPreference>();
        mVpnProfileList = new ArrayList<VpnProfile>();
        mVpnProfileList = new ArrayList<VpnProfile>();
        mVpnListContainer.removeAll();


        File root = new File(PROFILES_ROOT);
        File root = new File(PROFILES_ROOT);
        String[] dirs = root.list();
        String[] dirs = root.list();
@@ -476,6 +553,7 @@ public class VpnSettings extends PreferenceActivity {
            if (!f.exists()) continue;
            if (!f.exists()) continue;
            try {
            try {
                VpnProfile p = deserialize(f);
                VpnProfile p = deserialize(f);
                if (p == null) continue;
                if (!checkIdConsistency(dir, p)) continue;
                if (!checkIdConsistency(dir, p)) continue;


                mVpnProfileList.add(p);
                mVpnProfileList.add(p);
@@ -485,7 +563,6 @@ public class VpnSettings extends PreferenceActivity {
            }
            }
        }
        }
        disableProfilePreferencesIfOneActive();
        disableProfilePreferencesIfOneActive();
        checkVpnConnectionStatusInBackground();
    }
    }


    private void checkVpnConnectionStatusInBackground() {
    private void checkVpnConnectionStatusInBackground() {
@@ -502,7 +579,7 @@ public class VpnSettings extends PreferenceActivity {
    // are consistent.
    // are consistent.
    private boolean checkIdConsistency(String dirName, VpnProfile p) {
    private boolean checkIdConsistency(String dirName, VpnProfile p) {
        if (!dirName.equals(p.getId())) {
        if (!dirName.equals(p.getId())) {
            Log.v(TAG, "ID inconsistent: " + dirName + " vs " + p.getId());
            Log.d(TAG, "ID inconsistent: " + dirName + " vs " + p.getId());
            return false;
            return false;
        } else {
        } else {
            return true;
            return true;
@@ -517,7 +594,8 @@ public class VpnSettings extends PreferenceActivity {
            ois.close();
            ois.close();
            return p;
            return p;
        } catch (ClassNotFoundException e) {
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
            Log.d(TAG, "deserialize a profile", e);
            return null;
        }
        }
    }
    }


@@ -582,4 +660,100 @@ public class VpnSettings extends PreferenceActivity {
            }
            }
        }
        }
    }
    }

    // to catch saved user name in the connect dialog
    private class VpnProfileWrapper extends VpnProfile {
        private VpnProfile mProfile;

        VpnProfileWrapper(VpnProfile p) {
            mProfile = p;
        }

        @Override
        public void setSavedUsername(String name) {
            if ((name != null) && !name.equals(mProfile.getSavedUsername())) {
                mProfile.setSavedUsername(name);
                try {
                    saveProfileToStorage(mProfile);
                } catch (IOException e) {
                    Log.d(TAG, "save username", e);
                    // harmless
                }
            }
        }

        @Override
        public String getSavedUsername() {
            return mProfile.getSavedUsername();
        }

        @Override
        public void writeToParcel(Parcel parcel, int flags) {
            mProfile.writeToParcel(parcel, flags);
        }

        @Override
        public void setName(String name) {
        }

        @Override
        public String getName() {
            return mProfile.getName();
        }

        @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();
        }
    }
}
}