Merge "Make settings flow work for OAuth" into ub-mail-master

This commit is contained in:
Martin Hibdon 2014-02-14 19:59:25 +00:00 committed by Android (Google) Code Review
commit 38fd08e470
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;