am da8836a7: Give warnings if dupe account created:

Merge commit 'da8836a76cd8a6eaa7e3693eeacc6393870b2066' into eclair-plus-aosp

* commit 'da8836a76cd8a6eaa7e3693eeacc6393870b2066':
  Give warnings if dupe account created:
This commit is contained in:
Andrew Stadler 2009-09-15 16:43:26 -07:00 committed by Android Git Automerger
commit 59ce4670b5
9 changed files with 307 additions and 12 deletions

View File

@ -271,6 +271,13 @@
<!-- Toast when we can't build a URI from the given email & password -->
<!-- Note, the error message in the toast is purposefully vague, because I *don't* know exactly what's wrong. -->
<string name="account_setup_username_password_toast">Please type a valid email address and password.</string>
<!-- Title of dialog shown when a duplicate account is created -->
<string name="account_duplicate_dlg_title">Duplicate Account</string>
<!-- Message of dialog shown when a duplicate account is created. The display name of
the duplicate account is displayed. -->
<string name="account_duplicate_dlg_message_fmt">
This login is already in use for the account \"<xliff:g id="duplicate">%s</xliff:g>\".
</string>
<!-- Do Not Translate. Activity Title for check-settings screen -->
<string name="account_setup_check_settings_title"></string>

View File

@ -17,6 +17,10 @@
package com.android.email;
import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.AccountColumns;
import com.android.email.provider.EmailContent.HostAuth;
import com.android.email.provider.EmailContent.HostAuthColumns;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.MailboxColumns;
import com.android.email.provider.EmailContent.Message;
@ -338,4 +342,50 @@ public class Utility {
return mDefaultMailboxDrawable;
}
}
private final static String HOSTAUTH_WHERE_CREDENTIALS = HostAuthColumns.ADDRESS + " like ?"
+ " and " + HostAuthColumns.LOGIN + " like ?"
+ " and " + HostAuthColumns.PROTOCOL + " not like \"smtp\"";
private final static String ACCOUNT_WHERE_HOSTAUTH = AccountColumns.HOST_AUTH_KEY_RECV + "=?";
/**
* Look for an existing account with the same username & server
*
* @param context a system context
* @param allowAccountId this account Id will not trigger (when editing an existing account)
* @param hostName the server
* @param userLogin the user login string
* @result null = no dupes found. non-null = dupe account's display name
*/
public static String findDuplicateAccount(Context context, long allowAccountId, String hostName,
String userLogin) {
ContentResolver resolver = context.getContentResolver();
Cursor c = resolver.query(HostAuth.CONTENT_URI, HostAuth.ID_PROJECTION,
HOSTAUTH_WHERE_CREDENTIALS, new String[] { hostName, userLogin }, null);
try {
while (c.moveToNext()) {
long hostAuthId = c.getLong(HostAuth.ID_PROJECTION_COLUMN);
// Find account with matching hostauthrecv key, and return its display name
Cursor c2 = resolver.query(Account.CONTENT_URI, Account.ID_PROJECTION,
ACCOUNT_WHERE_HOSTAUTH, new String[] { Long.toString(hostAuthId) }, null);
try {
while (c2.moveToNext()) {
long accountId = c2.getLong(Account.ID_PROJECTION_COLUMN);
if (accountId != allowAccountId) {
Account account = Account.restoreAccountWithId(context, accountId);
if (account != null) {
return account.mDisplayName;
}
}
}
} finally {
c2.close();
}
}
} finally {
c.close();
}
return null;
}
}

View File

@ -459,6 +459,19 @@ public class AccountFolderList extends ListActivity
.create();
}
/**
* Update a cached dialog with current values (e.g. account name)
*/
@Override
public void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DIALOG_REMOVE_ACCOUNT:
AlertDialog alert = (AlertDialog) dialog;
alert.setMessage(getString(R.string.account_delete_dlg_instructions_fmt,
mSelectedContextAccount.getDisplayName()));
}
}
@Override
public boolean onContextItemSelected(MenuItem item) {
AdapterView.AdapterContextMenuInfo menuInfo =

View File

@ -18,7 +18,6 @@ package com.android.email.activity.setup;
import com.android.email.Email;
import com.android.email.EmailAddressValidator;
import com.android.email.Preferences;
import com.android.email.R;
import com.android.email.Utility;
import com.android.email.activity.Debug;
@ -33,10 +32,7 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.XmlResourceParser;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.Contacts;
import android.provider.Contacts.People.ContactMethods;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
@ -67,13 +63,14 @@ public class AccountSetupBasics extends Activity
private final static String EXTRA_EAS_FLOW = "com.android.email.extra.eas_flow";
private final static int DIALOG_NOTE = 1;
private final static int DIALOG_DUPLICATE_ACCOUNT = 2;
private final static String STATE_KEY_PROVIDER =
"com.android.email.AccountSetupBasics.provider";
// NOTE: If you change this value, confirm that the new interval exists in arrays.xml
private final static int DEFAULT_ACCOUNT_CHECK_INTERVAL = 15;
private Preferences mPrefs;
private EditText mEmailView;
private EditText mPasswordView;
private CheckBox mDefaultView;
@ -82,6 +79,7 @@ public class AccountSetupBasics extends Activity
private EmailContent.Account mAccount;
private Provider mProvider;
private boolean mEasFlowMode;
private String mDuplicateAccountName;
private EmailAddressValidator mEmailValidator = new EmailAddressValidator();
@ -104,7 +102,6 @@ public class AccountSetupBasics extends Activity
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.account_setup_basics);
mPrefs = Preferences.getPreferences(this);
mEmailView = (EditText)findViewById(R.id.account_email);
mPasswordView = (EditText)findViewById(R.id.account_password);
mDefaultView = (CheckBox)findViewById(R.id.account_default);
@ -237,10 +234,45 @@ public class AccountSetupBasics extends Activity
null)
.create();
}
} else if (id == DIALOG_DUPLICATE_ACCOUNT) {
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.account_duplicate_dlg_title)
.setMessage(getString(R.string.account_duplicate_dlg_message_fmt,
mDuplicateAccountName))
.setPositiveButton(R.string.okay_action,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dismissDialog(DIALOG_DUPLICATE_ACCOUNT);
}
})
.create();
}
return null;
}
/**
* Update a cached dialog with current values (e.g. account name)
*/
@Override
public void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DIALOG_NOTE:
if (mProvider != null && mProvider.note != null) {
AlertDialog alert = (AlertDialog) dialog;
alert.setMessage(mProvider.note);
}
break;
case DIALOG_DUPLICATE_ACCOUNT:
if (mDuplicateAccountName != null) {
AlertDialog alert = (AlertDialog) dialog;
alert.setMessage(getString(R.string.account_duplicate_dlg_message_fmt,
mDuplicateAccountName));
}
break;
}
}
private void finishAutoSetup() {
String email = mEmailView.getText().toString().trim();
String password = mPasswordView.getText().toString().trim();
@ -269,6 +301,15 @@ public class AccountSetupBasics extends Activity
outgoingUri = new URI(outgoingUriTemplate.getScheme(), outgoingUsername + ":"
+ password, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(),
outgoingUriTemplate.getPath(), null, null);
// Stop here if the login credentials duplicate an existing account
mDuplicateAccountName = Utility.findDuplicateAccount(this, -1,
incomingUri.getHost(), incomingUsername);
if (mDuplicateAccountName != null) {
this.showDialog(DIALOG_DUPLICATE_ACCOUNT);
return;
}
} catch (URISyntaxException use) {
/*
* If there is some problem with the URI we give up and go on to

View File

@ -22,6 +22,9 @@ import com.android.email.provider.EmailContent;
import com.android.email.provider.EmailContent.Account;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
@ -53,6 +56,8 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
private static final String EXTRA_MAKE_DEFAULT = "makeDefault";
private static final String EXTRA_EAS_FLOW = "easFlow";
private final static int DIALOG_DUPLICATE_ACCOUNT = 1;
private EditText mUsernameView;
private EditText mPasswordView;
private EditText mServerView;
@ -62,6 +67,8 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
private Button mNextButton;
private Account mAccount;
private boolean mMakeDefault;
private String mCacheLoginCredential;
private String mDuplicateAccountName;
public static void actionIncomingSettings(Activity fromActivity, Account account,
boolean makeDefault, boolean easFlowMode) {
@ -189,6 +196,45 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
outState.putParcelable(EXTRA_ACCOUNT, mAccount);
}
/**
* Prepare a cached dialog with current values (e.g. account name)
*/
@Override
public Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_DUPLICATE_ACCOUNT:
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.account_duplicate_dlg_title)
.setMessage(getString(R.string.account_duplicate_dlg_message_fmt,
mDuplicateAccountName))
.setPositiveButton(R.string.okay_action,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dismissDialog(DIALOG_DUPLICATE_ACCOUNT);
}
})
.create();
}
return null;
}
/**
* Update a cached dialog with current values (e.g. account name)
*/
@Override
public void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DIALOG_DUPLICATE_ACCOUNT:
if (mDuplicateAccountName != null) {
AlertDialog alert = (AlertDialog) dialog;
alert.setMessage(getString(R.string.account_duplicate_dlg_message_fmt,
mDuplicateAccountName));
}
break;
}
}
/**
* Check the values in the fields and decide if it makes sense to enable the "next" button
* NOTE: Does it make sense to extract & combine with similar code in AccountSetupIncoming?
@ -243,6 +289,7 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
if (userName.startsWith("\\")) {
userName = userName.substring(1);
}
mCacheLoginCredential = userName;
String userInfo = userName + ":" + mPasswordView.getText().toString().trim();
String host = mServerView.getText().toString().trim();
String path = null;
@ -267,6 +314,15 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
URI uri = getUri();
mAccount.setStoreUri(this, uri.toString());
mAccount.setSenderUri(this, uri.toString());
// Stop here if the login credentials duplicate an existing account
// (unless they duplicate the existing account, as they of course will)
mDuplicateAccountName = Utility.findDuplicateAccount(this, mAccount.mId,
uri.getHost(), mCacheLoginCredential);
if (mDuplicateAccountName != null) {
this.showDialog(DIALOG_DUPLICATE_ACCOUNT);
return;
}
} catch (URISyntaxException use) {
/*
* It's unrecoverable if we cannot create a URI from components that

View File

@ -21,6 +21,9 @@ import com.android.email.Utility;
import com.android.email.provider.EmailContent;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.text.Editable;
@ -55,6 +58,8 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
"imap", "imap+ssl", "imap+ssl+", "imap+tls", "imap+tls+"
};
private final static int DIALOG_DUPLICATE_ACCOUNT = 1;
private int mAccountPorts[];
private String mAccountSchemes[];
private EditText mUsernameView;
@ -67,6 +72,8 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
private Button mNextButton;
private EmailContent.Account mAccount;
private boolean mMakeDefault;
private String mCacheLoginCredential;
private String mDuplicateAccountName;
public static void actionIncomingSettings(Activity fromActivity, EmailContent.Account account,
boolean makeDefault) {
@ -254,6 +261,45 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
outState.putParcelable(EXTRA_ACCOUNT, mAccount);
}
/**
* Prepare a cached dialog with current values (e.g. account name)
*/
@Override
public Dialog onCreateDialog(int id) {
switch (id) {
case DIALOG_DUPLICATE_ACCOUNT:
return new AlertDialog.Builder(this)
.setIcon(android.R.drawable.ic_dialog_alert)
.setTitle(R.string.account_duplicate_dlg_title)
.setMessage(getString(R.string.account_duplicate_dlg_message_fmt,
mDuplicateAccountName))
.setPositiveButton(R.string.okay_action,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dismissDialog(DIALOG_DUPLICATE_ACCOUNT);
}
})
.create();
}
return null;
}
/**
* Update a cached dialog with current values (e.g. account name)
*/
@Override
public void onPrepareDialog(int id, Dialog dialog) {
switch (id) {
case DIALOG_DUPLICATE_ACCOUNT:
if (mDuplicateAccountName != null) {
AlertDialog alert = (AlertDialog) dialog;
alert.setMessage(getString(R.string.account_duplicate_dlg_message_fmt,
mDuplicateAccountName));
}
break;
}
}
/**
* Check the values in the fields and decide if it makes sense to enable the "next" button
* NOTE: Does it make sense to extract & combine with similar code in AccountSetupIncoming?
@ -331,10 +377,11 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
if (mAccountSchemes[securityType].startsWith("imap")) {
path = "/" + mImapPathPrefixView.getText().toString().trim();
}
String userName = mUsernameView.getText().toString().trim();
mCacheLoginCredential = userName;
URI uri = new URI(
mAccountSchemes[securityType],
mUsernameView.getText().toString().trim() + ":" +
mPasswordView.getText().toString().trim(),
userName + ":" + mPasswordView.getText().toString().trim(),
mServerView.getText().toString().trim(),
Integer.parseInt(mPortView.getText().toString().trim()),
path, // path
@ -348,6 +395,15 @@ public class AccountSetupIncoming extends Activity implements OnClickListener {
try {
URI uri = getUri();
mAccount.setStoreUri(this, uri.toString());
// Stop here if the login credentials duplicate an existing account
// (unless they duplicate the existing account, as they of course will)
mDuplicateAccountName = Utility.findDuplicateAccount(this, mAccount.mId,
uri.getHost(), mCacheLoginCredential);
if (mDuplicateAccountName != null) {
this.showDialog(DIALOG_DUPLICATE_ACCOUNT);
return;
}
} catch (URISyntaxException use) {
/*
* It's unrecoverable if we cannot create a URI from components that

View File

@ -1904,7 +1904,7 @@ public abstract class EmailContent {
static final String PASSWORD = "password";
// A domain or path, if required (used in IMAP and EAS)
static final String DOMAIN = "domain";
// Foreign key of the Account this is attached to
// DEPRECATED - Will not be set or stored
static final String ACCOUNT_KEY = "accountKey";
}
@ -1924,7 +1924,7 @@ public abstract class EmailContent {
public String mLogin;
public String mPassword;
public String mDomain;
public long mAccountKey;
public long mAccountKey; // DEPRECATED - Will not be set or stored
public static final int CONTENT_ID_COLUMN = 0;
public static final int CONTENT_PROTOCOL_COLUMN = 1;

View File

@ -18,6 +18,7 @@ package com.android.email.provider;
import com.android.email.provider.EmailContent.Account;
import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.HostAuth;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.Message;
@ -44,8 +45,8 @@ public class ProviderTestUtils extends Assert {
account.mSyncKey = "sync-key-" + name;
account.mSyncLookback = 1;
account.mSyncInterval = EmailContent.Account.CHECK_INTERVAL_NEVER;
account.mHostAuthKeyRecv = 2;
account.mHostAuthKeySend = 3;
account.mHostAuthKeyRecv = 0;
account.mHostAuthKeySend = 0;
account.mFlags = 4;
account.mIsDefault = true;
account.mCompatibilityUuid = "test-uid-" + name;
@ -60,6 +61,28 @@ public class ProviderTestUtils extends Assert {
return account;
}
/**
* Create a hostauth record for test purposes
*/
public static HostAuth setupHostAuth(String name, long accountId, boolean saveIt,
Context context) {
HostAuth hostAuth = new HostAuth();
hostAuth.mProtocol = "protocol-" + name;
hostAuth.mAddress = "address-" + name;
hostAuth.mPort = 100;
hostAuth.mFlags = 200;
hostAuth.mLogin = "login-" + name;
hostAuth.mPassword = "password-" + name;
hostAuth.mDomain = "domain-" + name;
hostAuth.mAccountKey = accountId;
if (saveIt) {
hostAuth.save(context);
}
return hostAuth;
}
/**
* Create a mailbox for test purposes
*/
@ -206,6 +229,26 @@ public class ProviderTestUtils extends Assert {
actual.mNewMessageCount);
}
/**
* Compare two hostauth records for equality
*/
public static void assertHostAuthEqual(String caller, HostAuth expect, HostAuth actual) {
if (expect == actual) {
return;
}
assertEmailContentEqual(caller, expect, actual);
assertEquals(caller + " mProtocol", expect.mProtocol, actual.mProtocol);
assertEquals(caller + " mAddress", expect.mAddress, actual.mAddress);
assertEquals(caller + " mPort", expect.mPort, actual.mPort);
assertEquals(caller + " mFlags", expect.mFlags, actual.mFlags);
assertEquals(caller + " mLogin", expect.mLogin, actual.mLogin);
assertEquals(caller + " mPassword", expect.mPassword, actual.mPassword);
assertEquals(caller + " mDomain", expect.mDomain, actual.mDomain);
// This field is dead and is not checked
// assertEquals(caller + " mAccountKey", expect.mAccountKey, actual.mAccountKey);
}
/**
* Compare two mailboxes for equality
*/

View File

@ -22,6 +22,7 @@ import com.android.email.provider.EmailContent.Attachment;
import com.android.email.provider.EmailContent.AttachmentColumns;
import com.android.email.provider.EmailContent.Body;
import com.android.email.provider.EmailContent.BodyColumns;
import com.android.email.provider.EmailContent.HostAuth;
import com.android.email.provider.EmailContent.Mailbox;
import com.android.email.provider.EmailContent.MailboxColumns;
import com.android.email.provider.EmailContent.Message;
@ -80,6 +81,34 @@ public class ProviderTests extends ProviderTestCase2<EmailProvider> {
ProviderTestUtils.assertAccountEqual("testAccountSave", account1, account2);
}
/**
* Test simple account save/retrieve with predefined hostauth records
*/
public void testAccountSaveHostAuth() {
Account account1 = ProviderTestUtils.setupAccount("account-hostauth", false, mMockContext);
// add hostauth data, which should be saved the first time
account1.mHostAuthRecv = ProviderTestUtils.setupHostAuth("account-hostauth-recv", -1, false,
mMockContext);
account1.mHostAuthSend = ProviderTestUtils.setupHostAuth("account-hostauth-send", -1, false,
mMockContext);
account1.save(mMockContext);
long account1Id = account1.mId;
// Confirm account reads back correctly
Account account1get = EmailContent.Account.restoreAccountWithId(mMockContext, account1Id);
ProviderTestUtils.assertAccountEqual("testAccountSave", account1, account1get);
// Confirm hostauth fields can be accessed & read back correctly
HostAuth hostAuth1get = EmailContent.HostAuth.restoreHostAuthWithId(mMockContext,
account1get.mHostAuthKeyRecv);
ProviderTestUtils.assertHostAuthEqual("testAccountSaveHostAuth-recv",
account1.mHostAuthRecv, hostAuth1get);
HostAuth hostAuth2get = EmailContent.HostAuth.restoreHostAuthWithId(mMockContext,
account1get.mHostAuthKeySend);
ProviderTestUtils.assertHostAuthEqual("testAccountSaveHostAuth-send",
account1.mHostAuthSend, hostAuth2get);
}
/**
* Simple test of account parceling. The rather tortuous path is to ensure that the
* account is really flattened all the way down to a parcel and back.