Fix auto-discover / account verification logic
New behavior: * There are two primary paths through this activity: * Edit existing: * Load existing values from account into fields * When user clicks 'next': * Confirm not a duplicate account * Try new values (check settings) * If new values are OK: * Write new values (save to provider) * finish() (pop to previous) * * Creating New: * Try Auto-discover to get details from server * If Auto-discover reports an authentication failure: * finish() (pop to previous, to re-enter username & password) * If Auto-discover succeeds: * write server's account details into account * Load values from account into fields * Confirm not a duplicate account * Try new values (check settings) * If new values are OK: * Write new values (save to provider) * Proceed to options screen * finish() (removes self from back stack) * Added unit test for new loadFields method Bug: 2412300
This commit is contained in:
parent
b854d05a89
commit
8bb0ee3b92
|
@ -54,6 +54,30 @@ import java.net.URISyntaxException;
|
||||||
* Requires SSL?
|
* Requires SSL?
|
||||||
* User (login)
|
* User (login)
|
||||||
* Password
|
* Password
|
||||||
|
*
|
||||||
|
* There are two primary paths through this activity:
|
||||||
|
* Edit existing:
|
||||||
|
* Load existing values from account into fields
|
||||||
|
* When user clicks 'next':
|
||||||
|
* Confirm not a duplicate account
|
||||||
|
* Try new values (check settings)
|
||||||
|
* If new values are OK:
|
||||||
|
* Write new values (save to provider)
|
||||||
|
* finish() (pop to previous)
|
||||||
|
*
|
||||||
|
* Creating New:
|
||||||
|
* Try Auto-discover to get details from server
|
||||||
|
* If Auto-discover reports an authentication failure:
|
||||||
|
* finish() (pop to previous, to re-enter username & password)
|
||||||
|
* If Auto-discover succeeds:
|
||||||
|
* write server's account details into account
|
||||||
|
* Load values from account into fields
|
||||||
|
* Confirm not a duplicate account
|
||||||
|
* Try new values (check settings)
|
||||||
|
* If new values are OK:
|
||||||
|
* Write new values (save to provider)
|
||||||
|
* Proceed to options screen
|
||||||
|
* finish() (removes self from back stack)
|
||||||
*/
|
*/
|
||||||
public class AccountSetupExchange extends Activity implements OnClickListener,
|
public class AccountSetupExchange extends Activity implements OnClickListener,
|
||||||
OnCheckedChangeListener {
|
OnCheckedChangeListener {
|
||||||
|
@ -151,57 +175,12 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
||||||
mAccount = (EmailContent.Account) savedInstanceState.getParcelable(EXTRA_ACCOUNT);
|
mAccount = (EmailContent.Account) savedInstanceState.getParcelable(EXTRA_ACCOUNT);
|
||||||
}
|
}
|
||||||
|
|
||||||
String username = null;
|
loadFields(mAccount);
|
||||||
String password = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
URI uri = new URI(mAccount.getStoreUri(this));
|
|
||||||
if (uri.getUserInfo() != null) {
|
|
||||||
String[] userInfoParts = uri.getUserInfo().split(":", 2);
|
|
||||||
username = userInfoParts[0];
|
|
||||||
if (userInfoParts.length > 1) {
|
|
||||||
password = userInfoParts[1];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (username != null) {
|
|
||||||
// Add a backslash to the start of the username, but only if the username has no
|
|
||||||
// backslash in it.
|
|
||||||
if (username.indexOf('\\') < 0) {
|
|
||||||
username = "\\" + username;
|
|
||||||
}
|
|
||||||
mUsernameView.setText(username);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (password != null) {
|
|
||||||
mPasswordView.setText(password);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uri.getScheme().startsWith("eas")) {
|
|
||||||
// any other setup from mAccount can go here
|
|
||||||
} else {
|
|
||||||
throw new Error("Unknown account type: " + mAccount.getStoreUri(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uri.getHost() != null) {
|
|
||||||
mServerView.setText(uri.getHost());
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean ssl = uri.getScheme().contains("ssl");
|
|
||||||
mSslSecurityView.setChecked(ssl);
|
|
||||||
mTrustCertificatesView.setChecked(uri.getScheme().contains("trustallcerts"));
|
|
||||||
mTrustCertificatesView.setVisibility(ssl ? View.VISIBLE : View.GONE);
|
|
||||||
|
|
||||||
} catch (URISyntaxException use) {
|
|
||||||
/*
|
|
||||||
* We should always be able to parse our own settings.
|
|
||||||
*/
|
|
||||||
throw new Error(use);
|
|
||||||
}
|
|
||||||
|
|
||||||
validateFields();
|
validateFields();
|
||||||
|
|
||||||
// If we've got a username and password and we're NOT editing, try autodiscover
|
// If we've got a username and password and we're NOT editing, try autodiscover
|
||||||
|
String username = mAccount.mHostAuthRecv.mLogin;
|
||||||
|
String password = mAccount.mHostAuthRecv.mPassword;
|
||||||
if (username != null && password != null &&
|
if (username != null && password != null &&
|
||||||
!Intent.ACTION_EDIT.equals(intent.getAction())) {
|
!Intent.ACTION_EDIT.equals(intent.getAction())) {
|
||||||
// NOTE: Disabling AutoDiscover is only used in unit tests
|
// NOTE: Disabling AutoDiscover is only used in unit tests
|
||||||
|
@ -264,11 +243,48 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Copy mAccount's values into UI fields
|
||||||
|
*/
|
||||||
|
/* package */ void loadFields(Account account) {
|
||||||
|
HostAuth hostAuth = account.mHostAuthRecv;
|
||||||
|
|
||||||
|
String userName = hostAuth.mLogin;
|
||||||
|
if (userName != null) {
|
||||||
|
// Add a backslash to the start of the username, but only if the username has no
|
||||||
|
// backslash in it.
|
||||||
|
if (userName.indexOf('\\') < 0) {
|
||||||
|
userName = "\\" + userName;
|
||||||
|
}
|
||||||
|
mUsernameView.setText(userName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostAuth.mPassword != null) {
|
||||||
|
mPasswordView.setText(hostAuth.mPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
String protocol = hostAuth.mProtocol;
|
||||||
|
if (protocol == null && !protocol.startsWith("eas")) {
|
||||||
|
throw new Error("Unknown account type: " + account.getStoreUri(this));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hostAuth.mAddress != null) {
|
||||||
|
mServerView.setText(hostAuth.mAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean ssl = 0 != (hostAuth.mFlags & HostAuth.FLAG_SSL);
|
||||||
|
boolean trustCertificates = 0 != (hostAuth.mFlags & HostAuth.FLAG_TRUST_ALL_CERTIFICATES);
|
||||||
|
mSslSecurityView.setChecked(ssl);
|
||||||
|
mTrustCertificatesView.setChecked(trustCertificates);
|
||||||
|
mTrustCertificatesView.setVisibility(ssl ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check the values in the fields and decide if it makes sense to enable the "next" button
|
* 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?
|
* NOTE: Does it make sense to extract & combine with similar code in AccountSetupIncoming?
|
||||||
|
* @return true if all fields are valid, false if fields are incomplete
|
||||||
*/
|
*/
|
||||||
private void validateFields() {
|
private boolean validateFields() {
|
||||||
boolean enabled = usernameFieldValid(mUsernameView)
|
boolean enabled = usernameFieldValid(mUsernameView)
|
||||||
&& Utility.requiredFieldValid(mPasswordView)
|
&& Utility.requiredFieldValid(mPasswordView)
|
||||||
&& Utility.requiredFieldValid(mServerView);
|
&& Utility.requiredFieldValid(mServerView);
|
||||||
|
@ -281,6 +297,7 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
||||||
}
|
}
|
||||||
mNextButton.setEnabled(enabled);
|
mNextButton.setEnabled(enabled);
|
||||||
Utility.setCompoundDrawablesAlpha(mNextButton, enabled ? 255 : 128);
|
Utility.setCompoundDrawablesAlpha(mNextButton, enabled ? 255 : 128);
|
||||||
|
return enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doOptions() {
|
private void doOptions() {
|
||||||
|
@ -326,22 +343,30 @@ public class AccountSetupExchange extends Activity implements OnClickListener,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (requestCode == AccountSetupCheckSettings.REQUEST_CODE_AUTO_DISCOVER) {
|
} else if (requestCode == AccountSetupCheckSettings.REQUEST_CODE_AUTO_DISCOVER) {
|
||||||
// The idea here is that it only matters if we've gotten a HostAuth back from the
|
// If authentication failed, exit immediately (to re-enter credentials)
|
||||||
// autodiscover service call. In all other cases, we can ignore the result
|
if (resultCode == AccountSetupCheckSettings.RESULT_AUTO_DISCOVER_AUTH_FAILED) {
|
||||||
|
finish();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If data was returned, populate the account, populate the UI fields, and proceed
|
||||||
|
// directly into account validation (to confirm the provided details actually work)
|
||||||
if (data != null) {
|
if (data != null) {
|
||||||
Parcelable p = data.getParcelableExtra("HostAuth");
|
Parcelable p = data.getParcelableExtra("HostAuth");
|
||||||
if (p != null) {
|
if (p != null) {
|
||||||
HostAuth hostAuth = (HostAuth)p;
|
HostAuth hostAuth = (HostAuth)p;
|
||||||
mAccount.mHostAuthSend = hostAuth;
|
mAccount.mHostAuthSend = hostAuth;
|
||||||
mAccount.mHostAuthRecv = hostAuth;
|
mAccount.mHostAuthRecv = hostAuth;
|
||||||
doOptions();
|
loadFields(mAccount);
|
||||||
|
if (validateFields()) {
|
||||||
|
// "click" next to launch server verification
|
||||||
|
onNext();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// If we've got an auth failed, we need to go back to the basic screen
|
|
||||||
// Otherwise, we just continue on with the Exchange setup screen
|
|
||||||
} else if (resultCode == AccountSetupCheckSettings.RESULT_AUTO_DISCOVER_AUTH_FAILED) {
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
}
|
// Otherwise, we'll fall through into the regular setup UI, with some fields
|
||||||
|
// pre-filled, and the user can do whatever they need to do.
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -18,12 +18,17 @@ package com.android.email.activity.setup;
|
||||||
|
|
||||||
import com.android.email.R;
|
import com.android.email.R;
|
||||||
import com.android.email.provider.EmailContent;
|
import com.android.email.provider.EmailContent;
|
||||||
|
import com.android.email.provider.ProviderTestUtils;
|
||||||
|
import com.android.email.provider.EmailContent.Account;
|
||||||
|
import com.android.email.provider.EmailContent.HostAuth;
|
||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.test.ActivityInstrumentationTestCase2;
|
import android.test.ActivityInstrumentationTestCase2;
|
||||||
import android.test.UiThreadTest;
|
import android.test.UiThreadTest;
|
||||||
import android.test.suitebuilder.annotation.MediumTest;
|
import android.test.suitebuilder.annotation.MediumTest;
|
||||||
|
import android.view.View;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
|
import android.widget.CheckBox;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,6 +41,8 @@ public class AccountSetupExchangeTests extends
|
||||||
private AccountSetupExchange mActivity;
|
private AccountSetupExchange mActivity;
|
||||||
private EditText mServerView;
|
private EditText mServerView;
|
||||||
private Button mNextButton;
|
private Button mNextButton;
|
||||||
|
private CheckBox mSslRequiredCheckbox;
|
||||||
|
private CheckBox mTrustAllCertificatesCheckbox;
|
||||||
|
|
||||||
public AccountSetupExchangeTests() {
|
public AccountSetupExchangeTests() {
|
||||||
super("com.android.email", AccountSetupExchange.class);
|
super("com.android.email", AccountSetupExchange.class);
|
||||||
|
@ -115,6 +122,45 @@ public class AccountSetupExchangeTests extends
|
||||||
mServerView.setText("serv$er.com");
|
mServerView.setText("serv$er.com");
|
||||||
assertFalse(mNextButton.isEnabled());
|
assertFalse(mNextButton.isEnabled());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test aspects of loadFields()
|
||||||
|
*
|
||||||
|
* TODO: More cases
|
||||||
|
*/
|
||||||
|
@UiThreadTest
|
||||||
|
public void testLoadFields() {
|
||||||
|
// The default URI has no SSL and no "trust"
|
||||||
|
getActivityAndFields();
|
||||||
|
assertFalse(mSslRequiredCheckbox.isChecked());
|
||||||
|
assertFalse(mTrustAllCertificatesCheckbox.isChecked());
|
||||||
|
assertFalse(mTrustAllCertificatesCheckbox.getVisibility() == View.VISIBLE);
|
||||||
|
|
||||||
|
// Setup host auth with variants of SSL enabled and check. This also enables the
|
||||||
|
// "trust certificates" checkbox (not checked, but visible now).
|
||||||
|
Account account =
|
||||||
|
ProviderTestUtils.setupAccount("account", false, mActivity.getBaseContext());
|
||||||
|
account.mHostAuthRecv =
|
||||||
|
ProviderTestUtils.setupHostAuth("hostauth", 1, false, mActivity.getBaseContext());
|
||||||
|
account.mHostAuthRecv.mFlags |= HostAuth.FLAG_SSL;
|
||||||
|
account.mHostAuthRecv.mFlags &= ~HostAuth.FLAG_TRUST_ALL_CERTIFICATES;
|
||||||
|
mActivity.loadFields(account);
|
||||||
|
assertTrue(mSslRequiredCheckbox.isChecked());
|
||||||
|
assertFalse(mTrustAllCertificatesCheckbox.isChecked());
|
||||||
|
assertTrue(mTrustAllCertificatesCheckbox.getVisibility() == View.VISIBLE);
|
||||||
|
|
||||||
|
// Setup host auth with variants of SSL enabled and check. This also enables the
|
||||||
|
// "trust certificates" checkbox (not checked, but visible now).
|
||||||
|
account.mHostAuthRecv.mFlags |= HostAuth.FLAG_TRUST_ALL_CERTIFICATES;
|
||||||
|
mActivity.loadFields(account);
|
||||||
|
assertTrue(mSslRequiredCheckbox.isChecked());
|
||||||
|
assertTrue(mTrustAllCertificatesCheckbox.isChecked());
|
||||||
|
assertTrue(mTrustAllCertificatesCheckbox.getVisibility() == View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO: Directly test validateFields() checking boolean result
|
||||||
|
*/
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the activity (which causes it to be started, using our intent) and get the UI fields
|
* Get the activity (which causes it to be started, using our intent) and get the UI fields
|
||||||
|
@ -123,6 +169,9 @@ public class AccountSetupExchangeTests extends
|
||||||
mActivity = getActivity();
|
mActivity = getActivity();
|
||||||
mServerView = (EditText) mActivity.findViewById(R.id.account_server);
|
mServerView = (EditText) mActivity.findViewById(R.id.account_server);
|
||||||
mNextButton = (Button) mActivity.findViewById(R.id.next);
|
mNextButton = (Button) mActivity.findViewById(R.id.next);
|
||||||
|
mSslRequiredCheckbox = (CheckBox) mActivity.findViewById(R.id.account_ssl);
|
||||||
|
mTrustAllCertificatesCheckbox =
|
||||||
|
(CheckBox) mActivity.findViewById(R.id.account_trust_certificates);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Reference in New Issue