Make settings flow work for OAuth

Now the password entry is removed from AccountSettingsBasics,
and the user is taken to either SignInActivity or AccountSetupType
after hitting the next button. This is a lot closer to the
desired setup flow as it allows for oauth signin.

Ideally this is not what we will ship for Algol, but it put us
in a state where we could ship if we had to.

Change-Id: I5b28bccd27c515572e4947ca877bd1772732507d
This commit is contained in:
Martin Hibdon 2014-02-13 17:06:33 -08:00
parent 1e05bc7d73
commit 87de90282b
16 changed files with 279 additions and 310 deletions

View File

@ -418,6 +418,8 @@ public class HostAuth extends EmailContent implements HostAuthColumns, Parcelabl
if (mCredential.equals(Credential.EMPTY)) {
mCredential = null;
}
} else {
mCredentialKey = -1;
}
}

View File

@ -33,7 +33,7 @@
android:text="@string/accounts_welcome"
android:textAppearance="@style/accountSetupInfoText" />
<TableLayout
android:id="@+id/email_password_table"
android:id="@+id/email_table"
android:layout_below="@+id/instructions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@ -54,21 +54,5 @@
android:inputType="textEmailAddress"
android:imeOptions="actionNext" />
</TableRow>
<TableRow android:paddingTop="16dip" >
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:layout_marginRight="16dip"
android:text="@string/account_setup_basics_password_label"
android:textAppearance="@style/accountSetupLabelText" />
<EditText
android:id="@+id/account_password"
android:layout_height="wrap_content"
android:layout_width="@dimen/setup_credentials_input_width"
android:contentDescription="@string/account_setup_basics_password_label"
android:inputType="textPassword"
android:imeOptions="actionDone"
android:nextFocusDown="@+id/next" />
</TableRow>
</TableLayout>
</RelativeLayout>

View File

@ -47,33 +47,28 @@
<!-- Buttons below -->
<!-- In order to show these buttons above the IME keyboard, we need to special case the to
padding to a smaller height. -->
<LinearLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/common"
android:orientation="horizontal"
android:paddingTop="16dip"
android:paddingBottom="@dimen/settings_buttons_padding_bottom"
>
<Button
android:paddingBottom="@dimen/settings_buttons_padding_bottom">
<TextView
android:id="@+id/manual_setup"
style="@style/accountSetupButton"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:clickable="true"
android:layout_alignParentLeft="true"
android:layout_gravity="center_vertical"
android:text="@string/account_setup_basics_manual_setup_action" />
<Button
android:id="@+id/oauth_setup"
style="@style/accountSetupButton"
android:layout_width="0dip"
android:layout_weight="1"
android:text="@string/account_setup_basics_oauth_setup_action" />
<Button
<ImageButton
android:id="@+id/next"
style="@style/accountSetupButton"
android:layout_width="0dip"
android:layout_weight="1"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="@drawable/ic_nav_arrow_forward"
android:layout_alignParentRight="true"
android:text="@string/next_action" />
</LinearLayout>
</RelativeLayout>
</RelativeLayout>

View File

@ -41,13 +41,4 @@
android:layout_below="@+id/instructions"
android:layout_height="wrap_content"
android:layout_width="match_parent" />
<EditText
android:id="@+id/account_password"
android:hint="@string/account_setup_basics_password_label"
android:inputType="textPassword"
android:imeOptions="actionDone"
android:layout_below="@+id/account_email"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:nextFocusDown="@+id/next" />
</RelativeLayout>

View File

@ -13,34 +13,51 @@
See the License for the specific language governing permissions and
limitations under the License.
-->
<LinearLayout
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_centerVertical="true"
android:gravity="center"
android:orientation="vertical">
android:layout_centerVertical="true">
<Button
android:id="@+id/sign_in_with_google"
android:text="@string/sign_in_with_google"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/or_label"
android:text="@string/or_label"
android:layout_marginTop="24dip"
android:layout_marginBottom="24dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:id="@+id/oauth_group"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:orientation="vertical"
android:visibility="gone">
<Button
android:id="@+id/sign_in_with_google"
android:text="@string/sign_in_with_google"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:id="@+id/or_label"
android:text="@string/or_label"
android:layout_marginTop="24dip"
android:layout_marginBottom="24dip"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/imap_password"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:hint="@string/account_setup_incoming_password_label"
android:layout_gravity="center"
android:inputType="textPassword"
android:imeOptions="actionNext"/>
</LinearLayout>
<EditText
android:id="@+id/account_password"
android:id="@+id/regular_password"
android:layout_height="wrap_content"
android:layout_width="match_parent"
android:hint="@string/account_setup_incoming_password_label"
android:inputType="textPassword"
android:imeOptions="actionNext"/>
</LinearLayout>
android:layout_alignParentTop="true"
android:imeOptions="actionNext"
android:visibility="gone"/>
</RelativeLayout>

View File

@ -193,7 +193,7 @@
<!-- Title of screen when trying to get oauth authentication -->
<string name="oauth_authentication_title" translatable="false">OAuth authentication</string>
<!-- Title of screen to choose with authentication to use -->
<string name="sign_in_title">IMAP Authentication</string>
<string name="sign_in_title">Sign in</string>
<!-- Headline of screen when setting up new email account (large text over divider)
[CHAR LIMIT=none] -->
<string name="account_setup_basics_headline">Email account</string>

View File

@ -442,8 +442,8 @@ public class AccountCheckSettingsFragment extends Fragment {
try {
if ((mMode & SetupDataFragment.CHECK_AUTODISCOVER) != 0) {
if (isCancelled()) return null;
LogUtils.d(Logging.LOG_TAG, "Begin auto-discover for %s", mCheckEmail);
publishProgress(STATE_CHECK_AUTODISCOVER);
LogUtils.d(Logging.LOG_TAG, "Begin auto-discover for " + mCheckEmail);
final Store store = Store.getInstance(mAccount, mContext);
final Bundle result = store.autoDiscover(mContext, mCheckEmail, mCheckPassword);
// Result will be one of:
@ -617,7 +617,7 @@ public class AccountCheckSettingsFragment extends Fragment {
// Belt and suspenders here; there should always be a non-empty array here
String[] unsupportedPolicies = (String[]) ex.getExceptionData();
if (unsupportedPolicies == null) {
LogUtils.w(TAG, "No data for unsupported policies?");
LogUtils.w(Logging.LOG_TAG, "No data for unsupported policies");
break;
}
// Build a string, concatenating policies we don't support

View File

@ -39,6 +39,8 @@ import android.text.format.DateUtils;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.android.email.R;
@ -84,7 +86,7 @@ import java.net.URISyntaxException;
* Note: Exchange accounts that require device security policies cannot be created automatically.
*/
public class AccountSetupBasics extends AccountSetupActivity
implements OnClickListener, AccountCheckSettingsFragment.Callbacks {
implements OnClickListener {
// STOPSHIP: Set to false before shipping, logs PII
private final static boolean ENTER_DEBUG_SCREEN = true;
@ -104,22 +106,12 @@ public class AccountSetupBasics extends AccountSetupActivity
private static final String STATE_KEY_PROVIDER = "AccountSetupBasics.provider";
public static final int REQUEST_OAUTH = 1;
public static final int RESULT_OAUTH_SUCCESS = 0;
public static final int RESULT_OAUTH_USER_CANCELED = -1;
public static final int RESULT_OAUTH_FAILURE = -2;
// Support for UI
private Provider mProvider;
private Button mManualButton;
// TODO: This is a temporary hack to allow us to start testing OAuth flow. It should be
// removed once we have the new setup UI working.
private Button mOAuthButton;
private Button mNextButton;
private TextView mManualButton;
private ImageButton mNextButton;
private boolean mNextButtonInhibit;
private boolean mPaused;
private boolean mReportAccountAuthenticatorError;
private static final int OWNER_NAME_LOADER_ID = 0;
private String mOwnerName;
@ -136,33 +128,6 @@ public class AccountSetupBasics extends AccountSetupActivity
fromActivity.startActivity(i);
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_OAUTH) {
if (resultCode == RESULT_OAUTH_SUCCESS) {
final String accessToken = data.getStringExtra(
OAuthAuthenticationActivity.EXTRA_OAUTH_ACCESS_TOKEN);
final String refreshToken = data.getStringExtra(
OAuthAuthenticationActivity.EXTRA_OAUTH_REFRESH_TOKEN);
final int expiresInSeconds = data.getIntExtra(
OAuthAuthenticationActivity.EXTRA_OAUTH_EXPIRES_IN, 0);
finishOAuthSetup(accessToken, refreshToken, expiresInSeconds);
} else if (resultCode == RESULT_OAUTH_FAILURE
|| resultCode == RESULT_OAUTH_USER_CANCELED) {
// TODO: STOPSHIP: This setup UI is not correct, we need to figure out what to do
// in case of errors and have localized strings.
Toast.makeText(AccountSetupBasics.this,
"Failed to get token", Toast.LENGTH_LONG).show();
} else {
LogUtils.wtf(Logging.LOG_TAG, "Unknown result code from OAUTH: %d", resultCode);
}
} else {
LogUtils.e(Logging.LOG_TAG, "Unknown request code for onActivityResult in"
+ " AccountSetupBasics: %d", requestCode);
}
}
/**
* This generates setup data that can be used to start a self-contained account creation flow
* for exchange accounts.
@ -252,18 +217,14 @@ public class AccountSetupBasics extends AccountSetupActivity
setContentView(R.layout.account_setup_basics);
// Configure buttons
mManualButton = UiUtilities.getView(this, R.id.manual_setup);
mOAuthButton = UiUtilities.getView(this, R.id.oauth_setup);
mNextButton = UiUtilities.getView(this, R.id.next);
mManualButton.setVisibility(View.VISIBLE);
mManualButton.setOnClickListener(this);
mOAuthButton.setOnClickListener(this);
mNextButton.setOnClickListener(this);
// Force disabled until validator notifies otherwise
setProceedButtonsEnabled(false);
setOAuthButtonEnabled(false);
// Lightweight debounce while Async tasks underway
mNextButtonInhibit = false;
@ -277,7 +238,7 @@ public class AccountSetupBasics extends AccountSetupActivity
// the account manager. Most exit paths represent an failed or abandoned setup,
// so the default is to report the error. Success will be reported by the code in
// AccountSetupOptions that commits the finally created account.
mReportAccountAuthenticatorError = true;
mSetupData.setReportAccountAuthenticationError(true);
}
// Handle force account creation immediately (now that fragment is set up)
@ -303,7 +264,8 @@ public class AccountSetupBasics extends AccountSetupActivity
}
forceCreateAccount(email, user, incoming, outgoing);
// calls finish
onCheckSettingsComplete(AccountCheckSettingsFragment.CHECK_SETTINGS_OK, mSetupData);
// XXX disabled for now, we don't finish account setup in this activity anymore.
// onCheckSettingsComplete(AccountCheckSettingsFragment.CHECK_SETTINGS_OK, mSetupData);
return;
}
@ -353,7 +315,7 @@ public class AccountSetupBasics extends AccountSetupActivity
public void finish() {
// If the account manager initiated the creation, and success was not reported,
// then we assume that we're giving up (for any reason) - report failure.
if (mReportAccountAuthenticatorError) {
if (mSetupData.getReportAccountAuthenticationError()) {
final AccountAuthenticatorResponse authenticatorResponse =
mSetupData.getAccountAuthenticatorResponse();
if (authenticatorResponse != null) {
@ -388,29 +350,6 @@ public class AccountSetupBasics extends AccountSetupActivity
case R.id.manual_setup:
onManualSetup(false);
break;
case R.id.oauth_setup:
// TODO: This is a temporary hack to allow oauth flow to be shown. It should be
// removed when the real account setup flow is implemented.
// TODO: Also note that this check reads and parses the xml file each time. This
// should probably get cached somewhere.
final String email = getBasicsFragment().getEmail();
final String[] emailParts = email.split("@");
final String domain = emailParts[1].trim();
Provider provider = AccountSettingsUtils.findProviderForDomain(this, domain);
if (provider == null) {
// Maybe this is a dasher email address, just try to authenticate using google.
// TODO STOPSHIP: at some point, the UI needs to display something like
// "authenticate using google.com". For now, since the only oauth provider
// we support is google, we'll just assume that is the provider.
provider = AccountSettingsUtils.findProviderForDomain(this, "google.com");
}
if (provider != null && provider.oauth != null) {
final Intent i = new Intent(this, OAuthAuthenticationActivity.class);
i.putExtra(OAuthAuthenticationActivity.EXTRA_EMAIL_ADDRESS, email);
i.putExtra(OAuthAuthenticationActivity.EXTRA_PROVIDER, provider.oauth);
startActivityForResult(i, REQUEST_OAUTH);
}
break;
}
}
@ -425,62 +364,9 @@ public class AccountSetupBasics extends AccountSetupActivity
* Finish the auto setup process, in some cases after showing a warning dialog.
*/
private void finishAutoSetup() {
final AccountSetupBasicsFragment basicsFragment = getBasicsFragment();
final String email = basicsFragment.getEmail();
final String password = basicsFragment.getPassword();
try {
mProvider.expandTemplates(email);
final Account account = mSetupData.getAccount();
final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
HostAuth.setHostAuthFromString(recvAuth, mProvider.incomingUri);
recvAuth.setLogin(mProvider.incomingUsername, password);
final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(this,
recvAuth.mProtocol);
recvAuth.mPort =
((recvAuth.mFlags & HostAuth.FLAG_SSL) != 0) ? info.portSsl : info.port;
final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
HostAuth.setHostAuthFromString(sendAuth, mProvider.outgoingUri);
sendAuth.setLogin(mProvider.outgoingUsername, password);
// Populate the setup data, assuming that the duplicate account check will succeed
populateSetupData(getOwnerName(), email);
// Stop here if the login credentials duplicate an existing account
// Launch an Async task to do the work
new DuplicateCheckTask(this, email, true)
.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
} catch (URISyntaxException e) {
/*
* If there is some problem with the URI we give up and go on to manual setup.
* Technically speaking, AutoDiscover is OK here, since the user clicked "Next"
* to get here. This will not happen in practice because we don't expect to
* find any EAS accounts in the providers list.
*/
onManualSetup(true);
}
}
/**
* Finish the oauth setup process.
*/
private void finishOAuthSetup(final String accessToken, final String refreshToken,
int expiresInSeconds) {
final String email = getBasicsFragment().getEmail();
final String[] emailParts = email.split("@");
final String domain = emailParts[1].trim();
mProvider = AccountSettingsUtils.findProviderForDomain(this, domain);
if (mProvider == null) {
// Right now the only provider we support is google. Assume that domain for now.
// This will let us authenticate dasher accounts.
// TODO: STOPSHIP: we really need UI to allow this user to pick
// "authenticate with google" or some such.
mProvider = AccountSettingsUtils.findProviderForDomain(this, "google.com");
}
try {
mProvider.expandTemplates(email);
@ -489,16 +375,6 @@ public class AccountSetupBasics extends AccountSetupActivity
final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
HostAuth.setHostAuthFromString(recvAuth, mProvider.incomingUri);
recvAuth.setLogin(mProvider.incomingUsername, null);
Credential cred = recvAuth.getOrCreateCredential(this);
cred.mProviderId = mProvider.oauth;
cred.mAccessToken = accessToken;
cred.mRefreshToken = refreshToken;
cred.mExpiration = System.currentTimeMillis() +
expiresInSeconds * DateUtils.SECOND_IN_MILLIS;
// TODO: For now, assume that we will use SSL because that's what
// gmail wants. This needs to be parameterized from providers.xml
recvAuth.mFlags |= HostAuth.FLAG_SSL;
recvAuth.mFlags |= HostAuth.FLAG_OAUTH;
final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(this,
recvAuth.mProtocol);
@ -508,9 +384,6 @@ public class AccountSetupBasics extends AccountSetupActivity
final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
HostAuth.setHostAuthFromString(sendAuth, mProvider.outgoingUri);
sendAuth.setLogin(mProvider.outgoingUsername, null);
sendAuth.mCredential = cred;
sendAuth.mFlags |= HostAuth.FLAG_SSL;
sendAuth.mFlags |= HostAuth.FLAG_OAUTH;
// Populate the setup data, assuming that the duplicate account check will succeed
populateSetupData(getOwnerName(), email);
@ -558,21 +431,18 @@ public class AccountSetupBasics extends AccountSetupActivity
mNextButtonInhibit = false;
// Exit immediately if the user left before we finished
if (mPaused) return;
// Show duplicate account warning, or proceed
// Show duplicate account warning, or proceed.
if (duplicateAccountName != null) {
final DuplicateAccountDialogFragment dialogFragment =
DuplicateAccountDialogFragment.newInstance(duplicateAccountName);
dialogFragment.show(getFragmentManager(), DuplicateAccountDialogFragment.TAG);
} else {
if (mAutoSetup) {
final AccountCheckSettingsFragment checkerFragment =
AccountCheckSettingsFragment.newInstance(
SetupDataFragment.CHECK_INCOMING | SetupDataFragment.CHECK_OUTGOING,
null);
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
transaction.addToBackStack("back");
transaction.commit();
final Intent intent = new Intent(AccountSetupBasics.this, SignInActivity.class);
intent.putExtra(SignInActivity.EXTRA_FLOW_MODE_INITIAL, true);
intent.putExtra(SignInActivity.EXTRA_MANUAL_SETUP, false);
intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData);
startActivity(intent);
} else {
onManualSetup(true);
}
@ -621,7 +491,6 @@ public class AccountSetupBasics extends AccountSetupActivity
private void onManualSetup(boolean allowAutoDiscover) {
final AccountSetupBasicsFragment basicsFragment = getBasicsFragment();
final String email = basicsFragment.getEmail();
final String password = basicsFragment.getPassword();
final String[] emailParts = email.split("@");
final String user = emailParts[0].trim();
final String domain = emailParts[1].trim();
@ -630,9 +499,8 @@ public class AccountSetupBasics extends AccountSetupActivity
// Username: d@d.d
// Password: debug
if (ENTER_DEBUG_SCREEN) {
if ("d@d.d".equals(email) && "debug".equals(password)) {
if ("d@d.d".equals(email)) {
basicsFragment.setEmail("");
basicsFragment.setPassword("");
AccountSettings.actionSettingsWithDebug(this);
return;
}
@ -640,16 +508,20 @@ public class AccountSetupBasics extends AccountSetupActivity
final Account account = mSetupData.getAccount();
final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
recvAuth.setLogin(user, password);
recvAuth.setConnection(null, domain, HostAuth.PORT_UNKNOWN, HostAuth.FLAG_NONE);
recvAuth.setLogin(user, null);
final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
sendAuth.setLogin(user, password);
sendAuth.setConnection(null, domain, HostAuth.PORT_UNKNOWN, HostAuth.FLAG_NONE);
sendAuth.setLogin(user, null);
populateSetupData(getOwnerName(), email);
mSetupData.setAllowAutodiscover(allowAutoDiscover);
// FLAG: We should not launch the protocol picker if we are coming from device settings,
// (as opposed to in-app adding account.) If we come from device settings, the user has
// already explicitly chosen the account type.
AccountSetupType.actionSelectAccountType(this, mSetupData);
}
@ -703,37 +575,6 @@ public class AccountSetupBasics extends AccountSetupActivity
setDefaultsForProtocol(this, account);
}
/**
* Implements AccountCheckSettingsFragment.Callbacks
*
* This is used in automatic setup mode to jump directly down to the options screen.
*
* This is the only case where we finish() this activity but account setup is continuing,
* so we inhibit reporting any error back to the Account manager.
*/
@Override
public void onCheckSettingsComplete(int result, SetupDataFragment setupData) {
mSetupData = setupData;
if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
AccountSetupFinal.actionFinal(this, mSetupData);
mReportAccountAuthenticatorError = false;
finish();
}
}
/**
* Implements AccountCheckSettingsFragment.Callbacks
* This is overridden only by AccountSetupIncoming
*/
@Override
public void onAutoDiscoverComplete(int result, SetupDataFragment setupData) {
throw new IllegalStateException();
}
public void setOAuthButtonEnabled(boolean enabled) {
mOAuthButton.setEnabled(enabled);
}
public void setProceedButtonsEnabled(boolean enabled) {
mManualButton.setEnabled(enabled);
mNextButton.setEnabled(enabled);

View File

@ -32,7 +32,6 @@ import com.android.emailcommon.mail.Address;
public class AccountSetupBasicsFragment extends Fragment {
private EditText mEmailView;
private EditText mPasswordView;
public AccountSetupBasicsFragment() {}
@ -42,7 +41,6 @@ public class AccountSetupBasicsFragment extends Fragment {
final View view = inflater.inflate(R.layout.account_setup_basics_fragment, container, false);
mEmailView = UiUtilities.getView(view, R.id.account_email);
mPasswordView = UiUtilities.getView(view, R.id.account_password);
final TextWatcher textWatcher = new TextWatcher() {
@Override
@ -58,7 +56,6 @@ public class AccountSetupBasicsFragment extends Fragment {
};
mEmailView.addTextChangedListener(textWatcher);
mPasswordView.addTextChangedListener(textWatcher);
return view;
}
@ -72,11 +69,7 @@ public class AccountSetupBasicsFragment extends Fragment {
&& addresses.length == 1
&& !TextUtils.isEmpty(addresses[0].getAddress());
activity.setOAuthButtonEnabled(emailValid);
activity.setProceedButtonsEnabled(emailValid && !TextUtils.isEmpty(getPassword()));
// Warn (but don't prevent) if password has leading/trailing spaces
AccountSettingsUtils.checkPasswordSpaces(activity, mPasswordView);
activity.setProceedButtonsEnabled(emailValid);
}
public void setEmail(final String email) {
@ -86,12 +79,4 @@ public class AccountSetupBasicsFragment extends Fragment {
public String getEmail() {
return mEmailView.getText().toString().trim();
}
public void setPassword(final String password) {
mPasswordView.setText(password);
}
public String getPassword() {
return mPasswordView.getText().toString().trim();
}
}

View File

@ -590,7 +590,7 @@ public class AccountSetupIncomingFragment extends AccountServerBaseFragment
// Launch the signin activity.
// TODO: at some point we should just use the sign in fragment on the main setup activity.
final Intent intent = new Intent(getActivity(), SignInActivity.class);
intent.putExtra(SignInActivity.EXTRA_EMAIL, mSetupData.getAccount().mEmailAddress);
intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData);
startActivityForResult(intent, SIGN_IN_REQUEST);
}

View File

@ -419,7 +419,7 @@ public class AccountSetupOutgoingFragment extends AccountServerBaseFragment
// Launch the signin activity.
// TODO: at some point we should just use the sign in fragment on the main setup activity.
final Intent intent = new Intent(getActivity(), SignInActivity.class);
intent.putExtra(SignInActivity.EXTRA_EMAIL, mSetupData.getAccount().mEmailAddress);
intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData);
startActivityForResult(intent, SIGN_IN_REQUEST);
}
}

View File

@ -132,7 +132,17 @@ public class AccountSetupType extends AccountSetupActivity implements OnClickLis
}
recvAuth.mLogin = recvAuth.mLogin + "@" + recvAuth.mAddress;
AccountSetupBasics.setDefaultsForProtocol(this, account);
// XXX this launches the incoming activity. We should only be doing this
// if we are in full on manual mode I think.
AccountSetupIncoming.actionIncomingSettings(this, mSetupData);
// XXX This launches the signin activity.
// final Intent intent = new Intent(this, SignInActivity.class);
// intent.putExtra(SignInActivity.EXTRA_FLOW_MODE_INITIAL, true);
// intent.putExtra(SignInActivity.EXTRA_MANUAL_SETUP, false);
// intent.putExtra(SetupDataFragment.EXTRA_SETUP_DATA, mSetupData);
// startActivity(intent);
// Back from the incoming screen returns to AccountSetupBasics
finish();
}

View File

@ -46,6 +46,12 @@ public class OAuthAuthenticationActivity extends Activity implements
public static final String EXTRA_OAUTH_REFRESH_TOKEN = "refreshToken";
public static final String EXTRA_OAUTH_EXPIRES_IN = "expiresIn";
public static final int REQUEST_OAUTH = 1;
public static final int RESULT_OAUTH_SUCCESS = 0;
public static final int RESULT_OAUTH_USER_CANCELED = -1;
public static final int RESULT_OAUTH_FAILURE = -2;
private WebView mWv;
private OAuthProvider mProvider;
private String mAuthenticationCode;
@ -73,7 +79,7 @@ public class OAuthAuthenticationActivity extends Activity implements
if (error != null) {
final Intent intent = new Intent();
setResult(AccountSetupBasics.RESULT_OAUTH_USER_CANCELED, intent);
setResult(RESULT_OAUTH_USER_CANCELED, intent);
finish();
} else {
mAuthenticationCode = uri.getQueryParameter("code");
@ -122,7 +128,7 @@ public class OAuthAuthenticationActivity extends Activity implements
OAuthAuthenticationActivity.this);
}
// Set the result to cancelled until we have success.
setResult(AccountSetupBasics.RESULT_OAUTH_USER_CANCELED, null);
setResult(RESULT_OAUTH_USER_CANCELED, null);
}
@Override
@ -186,7 +192,7 @@ public class OAuthAuthenticationActivity extends Activity implements
intent.putExtra(EXTRA_OAUTH_ACCESS_TOKEN, data.mAccessToken);
intent.putExtra(EXTRA_OAUTH_REFRESH_TOKEN, data.mRefreshToken);
intent.putExtra(EXTRA_OAUTH_EXPIRES_IN, data.mExpiresInSeconds);
setResult(AccountSetupBasics.RESULT_OAUTH_SUCCESS, intent);
setResult(RESULT_OAUTH_SUCCESS, intent);
}
finish();
}

View File

@ -45,6 +45,8 @@ public class SetupDataFragment extends Fragment implements Parcelable {
private static final String SAVESTATE_POLICY = "policy";
private static final String SAVESTATE_ACCOUNTAUTHENTICATORRESPONSE =
"accountAuthenticatorResponse";
private static final String SAVESTATE_REPORT_AUTHENTICATION_ERROR =
"reportAuthenticationError";
// All access will be through getters/setters
private int mFlowMode = FLOW_MODE_NORMAL;
@ -56,6 +58,7 @@ public class SetupDataFragment extends Fragment implements Parcelable {
private boolean mAllowAutodiscover = true;
private Policy mPolicy;
private AccountAuthenticatorResponse mAccountAuthenticatorResponse = null;
private boolean mReportAccountAuthenticationError = false;
public interface SetupDataContainer {
public SetupDataFragment getSetupData();
@ -70,6 +73,7 @@ public class SetupDataFragment extends Fragment implements Parcelable {
mUsername = null;
mPassword = null;
mAccountAuthenticatorResponse = null;
mReportAccountAuthenticationError = false;
}
public SetupDataFragment(int flowMode) {
@ -100,6 +104,8 @@ public class SetupDataFragment extends Fragment implements Parcelable {
outState.putParcelable(SAVESTATE_POLICY, mPolicy);
outState.putParcelable(SAVESTATE_ACCOUNTAUTHENTICATORRESPONSE,
mAccountAuthenticatorResponse);
outState.putBoolean(SAVESTATE_REPORT_AUTHENTICATION_ERROR,
mReportAccountAuthenticationError);
}
@Override
@ -116,6 +122,8 @@ public class SetupDataFragment extends Fragment implements Parcelable {
mPolicy = savedInstanceState.getParcelable(SAVESTATE_POLICY);
mAccountAuthenticatorResponse =
savedInstanceState.getParcelable(SAVESTATE_ACCOUNTAUTHENTICATORRESPONSE);
mReportAccountAuthenticationError =
savedInstanceState.getBoolean(SAVESTATE_REPORT_AUTHENTICATION_ERROR, false);
}
setRetainInstance(true);
}
@ -194,6 +202,14 @@ public class SetupDataFragment extends Fragment implements Parcelable {
mAccountAuthenticatorResponse = accountAuthenticatorResponse;
}
public boolean getReportAccountAuthenticationError() {
return mReportAccountAuthenticationError;
}
public void setReportAccountAuthenticationError(final boolean report) {
mReportAccountAuthenticationError = report;
}
// Parcelable methods
@Override
public int describeContents() {

View File

@ -1,19 +1,28 @@
package com.android.email.activity.setup;
import android.app.Activity;
import android.app.FragmentTransaction;
import android.content.Intent;
import android.os.Bundle;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.View;
import android.widget.ImageButton;
import com.android.email.R;
import com.android.email.activity.UiUtilities;
import com.android.email.service.EmailServiceUtils;
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.Credential;
import com.android.emailcommon.provider.HostAuth;
import com.android.mail.utils.LogUtils;
public class SignInActivity extends Activity implements SignInFragment.SignInCallback,
View.OnClickListener {
public class SignInActivity extends AccountSetupActivity implements SignInFragment.SignInCallback,
View.OnClickListener, AccountCheckSettingsFragment.Callbacks{
public static final String EXTRA_EMAIL = "email";
public static final String EXTRA_MANUAL_SETUP = "manual";
public static final String EXTRA_FLOW_MODE_INITIAL = "initial";
public static final String EXTRA_PASSWORD = "password";
public static final String EXTRA_OAUTH_PROVIDER = "provider";
@ -29,16 +38,17 @@ public class SignInActivity extends Activity implements SignInFragment.SignInCal
public void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sign_in_activity);
final String emailAddress = mSetupData.getAccount().mEmailAddress;
mFragment = (SignInFragment)
getFragmentManager().findFragmentById(R.id.sign_in_fragment);
mFragment.setEmailAddress(getIntent().getStringExtra(EXTRA_EMAIL));
mFragment.setEmailAddress(emailAddress);
mFragment.setSignInCallback(this);
mNextButton = UiUtilities.getView(this, R.id.next);
mPrevButton = UiUtilities.getView(this, R.id.previous);
mNextButton.setOnClickListener(this);
mPrevButton.setOnClickListener(this);
onValidate();
// Assume canceled until we find out otherwise.
setResult(RESULT_CANCELED);
}
@ -46,20 +56,71 @@ public class SignInActivity extends Activity implements SignInFragment.SignInCal
@Override
public void onOAuthSignIn(final String providerId, final String accessToken,
final String refreshToken, final int expiresInSeconds) {
final Intent intent = new Intent();
intent.putExtra(EXTRA_OAUTH_PROVIDER, providerId);
intent.putExtra(EXTRA_OAUTH_ACCESS_TOKEN, accessToken);
intent.putExtra(EXTRA_OAUTH_REFRESH_TOKEN, refreshToken);
intent.putExtra(EXTRA_OAUTH_EXPIRES_IN_SECONDS, expiresInSeconds);
setResult(RESULT_OK, intent);
finish();
if (getIntent().getBooleanExtra(EXTRA_FLOW_MODE_INITIAL, false)) {
// On initial setup, we now try to validate the account.
final Account account = mSetupData.getAccount();
final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
final Credential cred = recvAuth.getOrCreateCredential(this);
cred.mProviderId = providerId;
cred.mAccessToken = accessToken;
cred.mRefreshToken = refreshToken;
cred.mExpiration = System.currentTimeMillis() +
expiresInSeconds * DateUtils.SECOND_IN_MILLIS;
// TODO: For now, assume that we will use SSL because that's what
// gmail wants. This needs to be parameterized from providers.xml
recvAuth.mFlags |= HostAuth.FLAG_SSL;
recvAuth.mFlags |= HostAuth.FLAG_OAUTH;
final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
sendAuth.mCredential = cred;
sendAuth.mFlags |= HostAuth.FLAG_SSL;
sendAuth.mFlags |= HostAuth.FLAG_OAUTH;
startAuthenticationCheck();
} else {
// On regular settings, we just return the auth info to the caller.
final Intent intent = new Intent();
intent.putExtra(EXTRA_OAUTH_PROVIDER, providerId);
intent.putExtra(EXTRA_OAUTH_ACCESS_TOKEN, accessToken);
intent.putExtra(EXTRA_OAUTH_REFRESH_TOKEN, refreshToken);
intent.putExtra(EXTRA_OAUTH_EXPIRES_IN_SECONDS, expiresInSeconds);
setResult(RESULT_OK, intent);
finish();
}
}
private void onPasswordSignIn(final String password) {
final Intent intent = new Intent();
intent.putExtra(EXTRA_PASSWORD, password);
setResult(RESULT_OK, intent);
finish();
private void onNext() {
final String password = mFragment.getPassword();
// This only applies for password authentication.
if (getIntent().getBooleanExtra(EXTRA_FLOW_MODE_INITIAL, false)) {
// On initial setup, we now try to validate the account.
final Account account = mSetupData.getAccount();
final HostAuth recvAuth = account.getOrCreateHostAuthRecv(this);
recvAuth.mPassword = password;
final HostAuth sendAuth = account.getOrCreateHostAuthSend(this);
sendAuth.mPassword = password;
startAuthenticationCheck();
} else {
// On regular settings, we just return the auth info to the caller.
final Intent intent = new Intent();
intent.putExtra(EXTRA_PASSWORD, password);
setResult(RESULT_OK, intent);
finish();
}
}
private void startAuthenticationCheck() {
final AccountCheckSettingsFragment checkerFragment =
AccountCheckSettingsFragment.newInstance(
SetupDataFragment.CHECK_INCOMING | SetupDataFragment.CHECK_OUTGOING,
null);
final FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(checkerFragment, AccountCheckSettingsFragment.TAG);
transaction.addToBackStack(null);
transaction.commit();
}
@Override
@ -70,9 +131,39 @@ public class SignInActivity extends Activity implements SignInFragment.SignInCal
@Override
public void onClick(View view) {
if (view == mNextButton) {
onPasswordSignIn(mFragment.getPassword());
onNext();
} else if (view == mPrevButton) {
onBackPressed();
}
}
/**
* Implements AccountCheckSettingsFragment.Callbacks
*
* This is used in automatic setup mode to jump directly down to the options screen.
*
* This is the only case where we finish() this activity but account setup is continuing,
* so we inhibit reporting any error back to the Account manager.
*/
@Override
public void onCheckSettingsComplete(int result, SetupDataFragment setupData) {
mSetupData = setupData;
if (result == AccountCheckSettingsFragment.CHECK_SETTINGS_OK) {
AccountSetupFinal.actionFinal(this, mSetupData);
mSetupData.setReportAccountAuthenticationError(false);
finish();
} else {
// FLAG: DO I need to do anything else here?
LogUtils.d(Logging.LOG_TAG, "failure on check setup");
}
}
/**
* Implements AccountCheckSettingsFragment.Callbacks
* This is overridden only by AccountSetupIncoming
*/
@Override
public void onAutoDiscoverComplete(int result, SetupDataFragment setupData) {
throw new IllegalStateException();
}
}

View File

@ -31,25 +31,26 @@ import android.widget.Toast;
import com.android.email.R;
import com.android.email.activity.UiUtilities;
import com.android.email.activity.setup.SetupDataFragment.SetupDataContainer;
import com.android.email.service.EmailServiceUtils;
import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
import com.android.emailcommon.Logging;
import com.android.emailcommon.VendorPolicyLoader.OAuthProvider;
import com.android.emailcommon.VendorPolicyLoader.Provider;
import com.android.emailcommon.provider.HostAuth;
import com.android.mail.utils.LogUtils;
import java.util.List;
public class SignInFragment extends Fragment implements OnClickListener {
public static final int REQUEST_OAUTH = 1;
public static final int RESULT_OAUTH_SUCCESS = 0;
public static final int RESULT_OAUTH_USER_CANCELED = -1;
public static final int RESULT_OAUTH_FAILURE = -2;
private View mOAuthGroup;
private View mOAuthButton;
private EditText mPasswordText;
private EditText mImapPasswordText;
private EditText mRegularPasswordText;
private TextWatcher mValidationTextWatcher;
private String mEmailAddress;
private EmailServiceInfo mServiceInfo;
private String mProviderId;
private SignInCallback mCallback;
private Context mContext;
@ -66,8 +67,10 @@ public class SignInFragment extends Fragment implements OnClickListener {
Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.sign_in_fragment, container, false);
mImapPasswordText = UiUtilities.getView(view, R.id.imap_password);
mRegularPasswordText = UiUtilities.getView(view, R.id.regular_password);
mOAuthGroup = UiUtilities.getView(view, R.id.oauth_group);
mOAuthButton = UiUtilities.getView(view, R.id.sign_in_with_google);
mPasswordText = UiUtilities.getView(view, R.id.account_password);
mOAuthButton.setOnClickListener(this);
// After any text edits, call validateFields() which enables or disables the Next button
@ -82,7 +85,8 @@ public class SignInFragment extends Fragment implements OnClickListener {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) { }
};
mPasswordText.addTextChangedListener(mValidationTextWatcher);
mImapPasswordText.addTextChangedListener(mValidationTextWatcher);
mRegularPasswordText.addTextChangedListener(mValidationTextWatcher);
return view;
}
@ -90,27 +94,47 @@ public class SignInFragment extends Fragment implements OnClickListener {
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
LogUtils.d(Logging.LOG_TAG, "onActivityCreated");
mContext = getActivity();
if (mContext instanceof SetupDataContainer) {
final SetupDataContainer setupContainer = (SetupDataContainer)mContext;
SetupDataFragment setupData = setupContainer.getSetupData();
final HostAuth hostAuth = setupData.getAccount().getOrCreateHostAuthRecv(mContext);
mServiceInfo = EmailServiceUtils.getServiceInfo(mContext,
hostAuth.mProtocol);
if (mServiceInfo.offerOAuth) {
mOAuthGroup.setVisibility(View.VISIBLE);
mRegularPasswordText.setVisibility(View.GONE);
} else {
mOAuthGroup.setVisibility(View.GONE);
mRegularPasswordText.setVisibility(View.VISIBLE);
}
}
validatePassword();
}
@Override
public void onDestroy() {
super.onDestroy();
mPasswordText.removeTextChangedListener(mValidationTextWatcher);
mPasswordText = null;
mImapPasswordText.removeTextChangedListener(mValidationTextWatcher);
mImapPasswordText = null;
mRegularPasswordText.removeTextChangedListener(mValidationTextWatcher);
mRegularPasswordText = null;
}
public void validatePassword() {
mCallback.onValidate();
// Warn (but don't prevent) if password has leading/trailing spaces
AccountSettingsUtils.checkPasswordSpaces(mContext, mPasswordText);
AccountSettingsUtils.checkPasswordSpaces(mContext, mImapPasswordText);
AccountSettingsUtils.checkPasswordSpaces(mContext, mRegularPasswordText);
}
@Override
public void onActivityResult(final int requestCode, final int resultCode,
final Intent data) {
if (requestCode == REQUEST_OAUTH) {
if (resultCode == RESULT_OAUTH_SUCCESS) {
if (requestCode == OAuthAuthenticationActivity.REQUEST_OAUTH) {
if (resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_SUCCESS) {
final String accessToken = data.getStringExtra(
OAuthAuthenticationActivity.EXTRA_OAUTH_ACCESS_TOKEN);
final String refreshToken = data.getStringExtra(
@ -118,10 +142,8 @@ public class SignInFragment extends Fragment implements OnClickListener {
final int expiresInSeconds = data.getIntExtra(
OAuthAuthenticationActivity.EXTRA_OAUTH_EXPIRES_IN, 0);
mCallback.onOAuthSignIn(mProviderId, accessToken, refreshToken, expiresInSeconds);
getActivity().finish();
} else if (resultCode == RESULT_OAUTH_FAILURE
|| resultCode == RESULT_OAUTH_USER_CANCELED) {
} else if (resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_FAILURE
|| resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_USER_CANCELED) {
LogUtils.i(Logging.LOG_TAG, "Result from oauth %d", resultCode);
} else {
LogUtils.wtf(Logging.LOG_TAG, "Unknown result code from OAUTH: %d", resultCode);
@ -145,7 +167,7 @@ public class SignInFragment extends Fragment implements OnClickListener {
final Intent i = new Intent(mContext, OAuthAuthenticationActivity.class);
i.putExtra(OAuthAuthenticationActivity.EXTRA_EMAIL_ADDRESS, mEmailAddress);
i.putExtra(OAuthAuthenticationActivity.EXTRA_PROVIDER, mProviderId);
startActivityForResult(i, REQUEST_OAUTH);
startActivityForResult(i, OAuthAuthenticationActivity.REQUEST_OAUTH);
}
}
}
@ -158,7 +180,16 @@ public class SignInFragment extends Fragment implements OnClickListener {
return mEmailAddress;
}
public String getPassword() { return mPasswordText.getText().toString(); }
public EmailServiceInfo setServiceInfo() {
return mServiceInfo;
}
public String getPassword() {
if (mServiceInfo.offerOAuth) {
return mImapPasswordText.getText().toString();
} else {
return mRegularPasswordText.getText().toString();
}
}
public void setSignInCallback(SignInCallback callback) {
mCallback = callback;