373 lines
16 KiB
Java
373 lines
16 KiB
Java
/*
|
|
* Copyright (C) 2014 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
package com.android.email.activity.setup;
|
|
|
|
import android.app.Activity;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.os.Bundle;
|
|
import android.text.Editable;
|
|
import android.text.TextUtils;
|
|
import android.text.TextWatcher;
|
|
import android.text.format.DateUtils;
|
|
import android.view.LayoutInflater;
|
|
import android.view.View;
|
|
import android.view.View.OnClickListener;
|
|
import android.view.ViewGroup;
|
|
import android.widget.EditText;
|
|
import android.widget.TextView;
|
|
|
|
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.email.view.CertificateSelector;
|
|
import com.android.email.view.CertificateSelector.HostCallback;
|
|
import com.android.emailcommon.Device;
|
|
import com.android.emailcommon.VendorPolicyLoader.OAuthProvider;
|
|
import com.android.emailcommon.provider.Credential;
|
|
import com.android.emailcommon.provider.HostAuth;
|
|
import com.android.emailcommon.utility.CertificateRequestor;
|
|
import com.android.mail.utils.LogUtils;
|
|
|
|
import java.io.IOException;
|
|
import java.util.List;
|
|
|
|
public class AccountSetupCredentialsFragment extends AccountSetupFragment
|
|
implements OnClickListener, HostCallback {
|
|
|
|
private static final int CERTIFICATE_REQUEST = 1000;
|
|
|
|
private static final String EXTRA_EMAIL = "email";
|
|
private static final String EXTRA_PROTOCOL = "protocol";
|
|
private static final String EXTRA_PASSWORD_FAILED = "password_failed";
|
|
private static final String EXTRA_STANDALONE = "standalone";
|
|
|
|
public static final String EXTRA_PASSWORD = "password";
|
|
public static final String EXTRA_CLIENT_CERT = "certificate";
|
|
public static final String EXTRA_OAUTH_PROVIDER = "provider";
|
|
public static final String EXTRA_OAUTH_ACCESS_TOKEN = "accessToken";
|
|
public static final String EXTRA_OAUTH_REFRESH_TOKEN = "refreshToken";
|
|
public static final String EXTRA_OAUTH_EXPIRES_IN_SECONDS = "expiresInSeconds";
|
|
|
|
private View mOAuthGroup;
|
|
private View mOAuthButton;
|
|
private EditText mImapPasswordText;
|
|
private EditText mRegularPasswordText;
|
|
private TextWatcher mValidationTextWatcher;
|
|
private TextView mPasswordWarningLabel;
|
|
private TextView mEmailConfirmationLabel;
|
|
private TextView mEmailConfirmation;
|
|
private CertificateSelector mClientCertificateSelector;
|
|
private View mDeviceIdSection;
|
|
private TextView mDeviceId;
|
|
|
|
private String mEmailAddress;
|
|
private boolean mOfferOAuth;
|
|
private boolean mOfferCerts;
|
|
private String mProviderId;
|
|
List<OAuthProvider> mOauthProviders;
|
|
|
|
private Context mAppContext;
|
|
|
|
private Bundle mResults;
|
|
|
|
public interface Callback extends AccountSetupFragment.Callback {
|
|
void onCredentialsComplete(Bundle results);
|
|
}
|
|
|
|
/**
|
|
* Create a new instance of this fragment with the appropriate email and protocol
|
|
* @param email login address for OAuth purposes
|
|
* @param protocol protocol of the service we're gathering credentials for
|
|
* @param clientCert alias of existing client cert
|
|
* @param passwordFailed true if the password attempt previously failed
|
|
* @param standalone true if this is not being inserted in the setup flow
|
|
* @return new fragment instance
|
|
*/
|
|
public static AccountSetupCredentialsFragment newInstance(final String email,
|
|
final String protocol, final String clientCert, final boolean passwordFailed,
|
|
final boolean standalone) {
|
|
final AccountSetupCredentialsFragment f = new AccountSetupCredentialsFragment();
|
|
final Bundle b = new Bundle(5);
|
|
b.putString(EXTRA_EMAIL, email);
|
|
b.putString(EXTRA_PROTOCOL, protocol);
|
|
b.putString(EXTRA_CLIENT_CERT, clientCert);
|
|
b.putBoolean(EXTRA_PASSWORD_FAILED, passwordFailed);
|
|
b.putBoolean(EXTRA_STANDALONE, standalone);
|
|
f.setArguments(b);
|
|
return f;
|
|
}
|
|
|
|
@Override
|
|
public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
|
|
final Bundle savedInstanceState) {
|
|
final boolean standalone = getArguments().getBoolean(EXTRA_STANDALONE);
|
|
final View view;
|
|
if (standalone) {
|
|
view = inflater.inflate(R.layout.account_credentials_fragment, container, false);
|
|
mNextButton = UiUtilities.getView(view, R.id.done);
|
|
mNextButton.setOnClickListener(this);
|
|
mPreviousButton = UiUtilities.getView(view, R.id.cancel);
|
|
mPreviousButton.setOnClickListener(this);
|
|
} else {
|
|
// TODO: real headline string instead of sign_in_title
|
|
view = inflateTemplatedView(inflater, container,
|
|
R.layout.account_setup_credentials_fragment, R.string.sign_in_title);
|
|
}
|
|
|
|
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_oauth);
|
|
mOAuthButton.setOnClickListener(this);
|
|
mClientCertificateSelector = UiUtilities.getView(view, R.id.client_certificate_selector);
|
|
mDeviceIdSection = UiUtilities.getView(view, R.id.device_id_section);
|
|
mDeviceId = UiUtilities.getView(view, R.id.device_id);
|
|
mPasswordWarningLabel = UiUtilities.getView(view, R.id.wrong_password_warning_label);
|
|
mEmailConfirmationLabel = UiUtilities.getView(view, R.id.email_confirmation_label);
|
|
mEmailConfirmation = UiUtilities.getView(view, R.id.email_confirmation);
|
|
|
|
mClientCertificateSelector.setHostCallback(this);
|
|
mClientCertificateSelector.setCertificate(getArguments().getString(EXTRA_CLIENT_CERT));
|
|
|
|
// After any text edits, call validateFields() which enables or disables the Next button
|
|
mValidationTextWatcher = new PasswordTextWatcher();
|
|
mImapPasswordText.addTextChangedListener(mValidationTextWatcher);
|
|
mRegularPasswordText.addTextChangedListener(mValidationTextWatcher);
|
|
|
|
return view;
|
|
}
|
|
|
|
private class PasswordTextWatcher implements TextWatcher {
|
|
@Override
|
|
public void afterTextChanged(Editable s) {
|
|
validatePassword();
|
|
}
|
|
|
|
@Override
|
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
|
|
@Override
|
|
public void onTextChanged(CharSequence s, int start, int before, int count) { }
|
|
}
|
|
|
|
@Override
|
|
public void onActivityCreated(final Bundle savedInstanceState) {
|
|
super.onActivityCreated(savedInstanceState);
|
|
|
|
mAppContext = getActivity().getApplicationContext();
|
|
mEmailAddress = getArguments().getString(EXTRA_EMAIL);
|
|
final String protocol = getArguments().getString(EXTRA_PROTOCOL);
|
|
mOauthProviders = AccountSettingsUtils.getAllOAuthProviders(mAppContext);
|
|
mOfferCerts = true;
|
|
if (protocol != null) {
|
|
final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mAppContext, protocol);
|
|
if (info != null) {
|
|
if (mOauthProviders.size() > 0) {
|
|
mOfferOAuth = info.offerOAuth;
|
|
}
|
|
mOfferCerts = info.offerCerts;
|
|
}
|
|
} else {
|
|
// For now, we might not know what protocol we're using, so just default to
|
|
// offering oauth
|
|
if (mOauthProviders.size() > 0) {
|
|
mOfferOAuth = true;
|
|
}
|
|
}
|
|
// We may want to disable OAuth during the new account setup flow, but allow it elsewhere
|
|
final boolean standalone = getArguments().getBoolean(EXTRA_STANDALONE);
|
|
final boolean skipOAuth = !standalone &&
|
|
getActivity().getResources().getBoolean(R.bool.skip_oauth_on_setup);
|
|
mOfferOAuth = mOfferOAuth && !skipOAuth;
|
|
|
|
mOAuthGroup.setVisibility(mOfferOAuth ? View.VISIBLE : View.GONE);
|
|
mRegularPasswordText.setVisibility(mOfferOAuth ? View.GONE : View.VISIBLE);
|
|
|
|
if (mOfferCerts) {
|
|
// TODO: Here we always offer certificates for any protocol that allows them (i.e.
|
|
// Exchange). But they will really only be available if we are using SSL security.
|
|
// Trouble is, first time through here, we haven't offered the user the choice of
|
|
// which security type to use.
|
|
mClientCertificateSelector.setVisibility(mOfferCerts ? View.VISIBLE : View.GONE);
|
|
mDeviceIdSection.setVisibility(mOfferCerts ? View.VISIBLE : View.GONE);
|
|
String deviceId = "";
|
|
try {
|
|
deviceId = Device.getDeviceId(getActivity());
|
|
} catch (IOException e) {
|
|
// Not required
|
|
}
|
|
mDeviceId.setText(deviceId);
|
|
}
|
|
final boolean passwordFailed = getArguments().getBoolean(EXTRA_PASSWORD_FAILED, false);
|
|
setPasswordFailed(passwordFailed);
|
|
validatePassword();
|
|
}
|
|
|
|
@Override
|
|
public void onDestroy() {
|
|
super.onDestroy();
|
|
if (mImapPasswordText != null) {
|
|
mImapPasswordText.removeTextChangedListener(mValidationTextWatcher);
|
|
mImapPasswordText = null;
|
|
}
|
|
if (mRegularPasswordText != null) {
|
|
mRegularPasswordText.removeTextChangedListener(mValidationTextWatcher);
|
|
mRegularPasswordText = null;
|
|
}
|
|
}
|
|
|
|
public void setPasswordFailed(final boolean failed) {
|
|
if (failed) {
|
|
mPasswordWarningLabel.setVisibility(View.VISIBLE);
|
|
mEmailConfirmationLabel.setVisibility(View.VISIBLE);
|
|
mEmailConfirmation.setVisibility(View.VISIBLE);
|
|
mEmailConfirmation.setText(mEmailAddress);
|
|
} else {
|
|
mPasswordWarningLabel.setVisibility(View.GONE);
|
|
mEmailConfirmationLabel.setVisibility(View.GONE);
|
|
mEmailConfirmation.setVisibility(View.GONE);
|
|
}
|
|
}
|
|
|
|
public void validatePassword() {
|
|
setNextButtonEnabled(!TextUtils.isEmpty(getPassword()));
|
|
}
|
|
|
|
@Override
|
|
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
|
|
if (requestCode == CERTIFICATE_REQUEST) {
|
|
if (resultCode == Activity.RESULT_OK) {
|
|
final String certAlias = data.getStringExtra(CertificateRequestor.RESULT_ALIAS);
|
|
if (certAlias != null) {
|
|
mClientCertificateSelector.setCertificate(certAlias);
|
|
}
|
|
} else {
|
|
LogUtils.e(LogUtils.TAG, "Unknown result from certificate request %d",
|
|
resultCode);
|
|
}
|
|
} else 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(
|
|
OAuthAuthenticationActivity.EXTRA_OAUTH_REFRESH_TOKEN);
|
|
final int expiresInSeconds = data.getIntExtra(
|
|
OAuthAuthenticationActivity.EXTRA_OAUTH_EXPIRES_IN, 0);
|
|
final Bundle results = new Bundle(4);
|
|
results.putString(EXTRA_OAUTH_PROVIDER, mProviderId);
|
|
results.putString(EXTRA_OAUTH_ACCESS_TOKEN, accessToken);
|
|
results.putString(EXTRA_OAUTH_REFRESH_TOKEN, refreshToken);
|
|
results.putInt(EXTRA_OAUTH_EXPIRES_IN_SECONDS, expiresInSeconds);
|
|
mResults = results;
|
|
final Callback callback = (Callback) getActivity();
|
|
callback.onCredentialsComplete(results);
|
|
} else if (resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_FAILURE
|
|
|| resultCode == OAuthAuthenticationActivity.RESULT_OAUTH_USER_CANCELED) {
|
|
LogUtils.i(LogUtils.TAG, "Result from oauth %d", resultCode);
|
|
} else {
|
|
LogUtils.wtf(LogUtils.TAG, "Unknown result code from OAUTH: %d", resultCode);
|
|
}
|
|
} else {
|
|
LogUtils.e(LogUtils.TAG, "Unknown request code for onActivityResult in"
|
|
+ " AccountSetupBasics: %d", requestCode);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onClick(final View view) {
|
|
final int viewId = view.getId();
|
|
if (viewId == R.id.sign_in_with_oauth) {
|
|
// TODO currently the only oauth provider we support is google.
|
|
// If we ever have more than 1 oauth provider, then we need to implement some sort
|
|
// of picker UI. For now, just always take the first oauth provider.
|
|
if (mOauthProviders.size() > 0) {
|
|
mProviderId = mOauthProviders.get(0).id;
|
|
final Intent i = new Intent(getActivity(), OAuthAuthenticationActivity.class);
|
|
i.putExtra(OAuthAuthenticationActivity.EXTRA_EMAIL_ADDRESS, mEmailAddress);
|
|
i.putExtra(OAuthAuthenticationActivity.EXTRA_PROVIDER, mProviderId);
|
|
startActivityForResult(i, OAuthAuthenticationActivity.REQUEST_OAUTH);
|
|
}
|
|
} else if (viewId == R.id.done) {
|
|
final Callback callback = (Callback) getActivity();
|
|
callback.onNextButton();
|
|
} else if (viewId == R.id.cancel) {
|
|
final Callback callback = (Callback) getActivity();
|
|
callback.onBackPressed();
|
|
} else {
|
|
super.onClick(view);
|
|
}
|
|
}
|
|
|
|
public String getPassword() {
|
|
if (mOfferOAuth) {
|
|
return mImapPasswordText.getText().toString();
|
|
} else {
|
|
return mRegularPasswordText.getText().toString();
|
|
}
|
|
}
|
|
|
|
public Bundle getCredentialResults() {
|
|
if (mResults != null) {
|
|
return mResults;
|
|
}
|
|
|
|
final Bundle results = new Bundle(2);
|
|
results.putString(EXTRA_PASSWORD, getPassword());
|
|
results.putString(EXTRA_CLIENT_CERT, getClientCertificate());
|
|
return results;
|
|
}
|
|
|
|
public static void populateHostAuthWithResults(final Context context, final HostAuth hostAuth,
|
|
final Bundle results) {
|
|
if (results == null) {
|
|
return;
|
|
}
|
|
final String password = results.getString(AccountSetupCredentialsFragment.EXTRA_PASSWORD);
|
|
if (!TextUtils.isEmpty(password)) {
|
|
hostAuth.mPassword = password;
|
|
hostAuth.removeCredential();
|
|
} else {
|
|
Credential cred = hostAuth.getOrCreateCredential(context);
|
|
cred.mProviderId = results.getString(
|
|
AccountSetupCredentialsFragment.EXTRA_OAUTH_PROVIDER);
|
|
cred.mAccessToken = results.getString(
|
|
AccountSetupCredentialsFragment.EXTRA_OAUTH_ACCESS_TOKEN);
|
|
cred.mRefreshToken = results.getString(
|
|
AccountSetupCredentialsFragment.EXTRA_OAUTH_REFRESH_TOKEN);
|
|
cred.mExpiration = System.currentTimeMillis()
|
|
+ results.getInt(
|
|
AccountSetupCredentialsFragment.EXTRA_OAUTH_EXPIRES_IN_SECONDS, 0)
|
|
* DateUtils.SECOND_IN_MILLIS;
|
|
hostAuth.mPassword = null;
|
|
}
|
|
hostAuth.mClientCertAlias = results.getString(EXTRA_CLIENT_CERT);
|
|
}
|
|
|
|
public String getClientCertificate() {
|
|
return mClientCertificateSelector.getCertificate();
|
|
}
|
|
|
|
@Override
|
|
public void onCertificateRequested() {
|
|
final Intent intent = new Intent(getString(R.string.intent_exchange_cert_action));
|
|
intent.setData(CertificateRequestor.CERTIFICATE_REQUEST_URI);
|
|
// We don't set EXTRA_HOST or EXTRA_PORT here because we don't know the final host/port
|
|
// that we're connecting to yet, and autodiscover might point us somewhere else
|
|
startActivityForResult(intent, CERTIFICATE_REQUEST);
|
|
}
|
|
}
|