Home | History | Annotate | Download | only in setup
      1 /*
      2  * Copyright (C) 2010 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.android.email.activity.setup;
     18 
     19 import android.app.Activity;
     20 import android.content.Context;
     21 import android.content.Intent;
     22 import android.content.Loader;
     23 import android.os.Bundle;
     24 import android.os.Parcel;
     25 import android.text.Editable;
     26 import android.text.TextUtils;
     27 import android.text.TextWatcher;
     28 import android.text.method.DigitsKeyListener;
     29 import android.view.LayoutInflater;
     30 import android.view.View;
     31 import android.view.ViewGroup;
     32 import android.view.inputmethod.EditorInfo;
     33 import android.widget.AdapterView;
     34 import android.widget.ArrayAdapter;
     35 import android.widget.EditText;
     36 import android.widget.Spinner;
     37 import android.widget.TextView;
     38 
     39 import com.android.email.R;
     40 import com.android.email.activity.UiUtilities;
     41 import com.android.email.activity.setup.AuthenticationView.AuthenticationCallback;
     42 import com.android.email.provider.AccountBackupRestore;
     43 import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
     44 import com.android.email.view.CertificateSelector;
     45 import com.android.email.view.CertificateSelector.HostCallback;
     46 import com.android.emailcommon.Device;
     47 import com.android.emailcommon.VendorPolicyLoader;
     48 import com.android.emailcommon.provider.Account;
     49 import com.android.emailcommon.provider.Credential;
     50 import com.android.emailcommon.provider.HostAuth;
     51 import com.android.emailcommon.utility.CertificateRequestor;
     52 import com.android.emailcommon.utility.Utility;
     53 import com.android.mail.ui.MailAsyncTaskLoader;
     54 import com.android.mail.utils.LogUtils;
     55 
     56 import java.io.IOException;
     57 import java.util.ArrayList;
     58 import java.util.List;
     59 
     60 /**
     61  * Provides UI for IMAP/POP account settings.
     62  *
     63  * This fragment is used by AccountSetupIncoming (for creating accounts) and by AccountSettingsXL
     64  * (for editing existing accounts).
     65  */
     66 public class AccountSetupIncomingFragment extends AccountServerBaseFragment
     67         implements HostCallback, AuthenticationCallback {
     68 
     69     private static final int CERTIFICATE_REQUEST = 0;
     70     private static final int SIGN_IN_REQUEST = 1;
     71 
     72     private final static String STATE_KEY_CREDENTIAL = "AccountSetupIncomingFragment.credential";
     73     private final static String STATE_KEY_LOADED = "AccountSetupIncomingFragment.loaded";
     74 
     75     private EditText mUsernameView;
     76     private AuthenticationView mAuthenticationView;
     77     private TextView mAuthenticationLabel;
     78     private TextView mServerLabelView;
     79     private EditText mServerView;
     80     private EditText mPortView;
     81     private Spinner mSecurityTypeView;
     82     private TextView mDeletePolicyLabelView;
     83     private Spinner mDeletePolicyView;
     84     private CertificateSelector mClientCertificateSelector;
     85     private View mDeviceIdSection;
     86     private View mImapPathPrefixSectionView;
     87     private EditText mImapPathPrefixView;
     88     private boolean mOAuthProviderPresent;
     89     // Delete policy as loaded from the device
     90     private int mLoadedDeletePolicy;
     91 
     92     private TextWatcher mValidationTextWatcher;
     93 
     94     // Support for lifecycle
     95     private boolean mLoaded;
     96     private String mCacheLoginCredential;
     97     private EmailServiceInfo mServiceInfo;
     98 
     99     public static AccountSetupIncomingFragment newInstance(boolean settingsMode) {
    100         final AccountSetupIncomingFragment f = new AccountSetupIncomingFragment();
    101         f.setArguments(getArgs(settingsMode));
    102         return f;
    103     }
    104 
    105     // Public no-args constructor needed for fragment re-instantiation
    106     public AccountSetupIncomingFragment() {}
    107 
    108     /**
    109      * Called to do initial creation of a fragment.  This is called after
    110      * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
    111      */
    112     @Override
    113     public void onCreate(Bundle savedInstanceState) {
    114         super.onCreate(savedInstanceState);
    115 
    116         if (savedInstanceState != null) {
    117             mCacheLoginCredential = savedInstanceState.getString(STATE_KEY_CREDENTIAL);
    118             mLoaded = savedInstanceState.getBoolean(STATE_KEY_LOADED, false);
    119         }
    120     }
    121 
    122     @Override
    123     public View onCreateView(LayoutInflater inflater, ViewGroup container,
    124             Bundle savedInstanceState) {
    125         final View view;
    126         if (mSettingsMode) {
    127             view = inflater.inflate(R.layout.account_settings_incoming_fragment, container, false);
    128         } else {
    129             view = inflateTemplatedView(inflater, container,
    130                     R.layout.account_setup_incoming_fragment,
    131                     R.string.account_setup_incoming_headline);
    132         }
    133 
    134         mUsernameView = UiUtilities.getView(view, R.id.account_username);
    135         mServerLabelView = UiUtilities.getView(view, R.id.account_server_label);
    136         mServerView = UiUtilities.getView(view, R.id.account_server);
    137         mPortView = UiUtilities.getView(view, R.id.account_port);
    138         mSecurityTypeView = UiUtilities.getView(view, R.id.account_security_type);
    139         mDeletePolicyLabelView = UiUtilities.getView(view, R.id.account_delete_policy_label);
    140         mDeletePolicyView = UiUtilities.getView(view, R.id.account_delete_policy);
    141         mImapPathPrefixSectionView = UiUtilities.getView(view, R.id.imap_path_prefix_section);
    142         mImapPathPrefixView = UiUtilities.getView(view, R.id.imap_path_prefix);
    143         mAuthenticationView = UiUtilities.getView(view, R.id.authentication_view);
    144         mClientCertificateSelector = UiUtilities.getView(view, R.id.client_certificate_selector);
    145         mDeviceIdSection = UiUtilities.getView(view, R.id.device_id_section);
    146         // Don't use UiUtilities here. In some configurations this view does not exist, and
    147         // UiUtilities throws an exception in this case.
    148         mAuthenticationLabel = (TextView)view.findViewById(R.id.authentication_label);
    149 
    150         // Updates the port when the user changes the security type. This allows
    151         // us to show a reasonable default which the user can change.
    152         mSecurityTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
    153             @Override
    154             public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
    155                 updatePortFromSecurityType();
    156             }
    157 
    158             @Override
    159             public void onNothingSelected(AdapterView<?> arg0) { }
    160         });
    161 
    162         // After any text edits, call validateFields() which enables or disables the Next button
    163         mValidationTextWatcher = new TextWatcher() {
    164             @Override
    165             public void afterTextChanged(Editable s) {
    166                 validateFields();
    167             }
    168 
    169             @Override
    170             public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
    171             @Override
    172             public void onTextChanged(CharSequence s, int start, int before, int count) { }
    173         };
    174 
    175         mUsernameView.addTextChangedListener(mValidationTextWatcher);
    176         mServerView.addTextChangedListener(mValidationTextWatcher);
    177         mPortView.addTextChangedListener(mValidationTextWatcher);
    178 
    179         // Only allow digits in the port field.
    180         mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
    181 
    182         // Additional setup only used while in "settings" mode
    183         onCreateViewSettingsMode(view);
    184 
    185         mAuthenticationView.setAuthenticationCallback(this);
    186 
    187         return view;
    188     }
    189 
    190     @Override
    191     public void onActivityCreated(Bundle savedInstanceState) {
    192         super.onActivityCreated(savedInstanceState);
    193         mClientCertificateSelector.setHostCallback(this);
    194 
    195         final Context context = getActivity();
    196         final SetupDataFragment.SetupDataContainer container =
    197                 (SetupDataFragment.SetupDataContainer) context;
    198         mSetupData = container.getSetupData();
    199         final Account account = mSetupData.getAccount();
    200         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
    201 
    202         // Pre-fill info as appropriate
    203         if (!mSetupData.isIncomingCredLoaded()) {
    204             recvAuth.mLogin = mSetupData.getEmail();
    205             AccountSetupCredentialsFragment.populateHostAuthWithResults(context, recvAuth,
    206                     mSetupData.getCredentialResults());
    207             final String[] emailParts = mSetupData.getEmail().split("@");
    208             final String domain = emailParts[1];
    209             recvAuth.setConnection(recvAuth.mProtocol, domain, HostAuth.PORT_UNKNOWN,
    210                     HostAuth.FLAG_NONE);
    211             mSetupData.setIncomingCredLoaded(true);
    212         }
    213 
    214         mServiceInfo = mSetupData.getIncomingServiceInfo(context);
    215 
    216         if (mServiceInfo.offerLocalDeletes) {
    217             SpinnerOption deletePolicies[] = {
    218                     new SpinnerOption(Account.DELETE_POLICY_NEVER,
    219                             context.getString(
    220                                     R.string.account_setup_incoming_delete_policy_never_label)),
    221                     new SpinnerOption(Account.DELETE_POLICY_ON_DELETE,
    222                             context.getString(
    223                                     R.string.account_setup_incoming_delete_policy_delete_label)),
    224             };
    225             ArrayAdapter<SpinnerOption> deletePoliciesAdapter =
    226                     new ArrayAdapter<SpinnerOption>(context,
    227                             android.R.layout.simple_spinner_item, deletePolicies);
    228             deletePoliciesAdapter.setDropDownViewResource(
    229                     android.R.layout.simple_spinner_dropdown_item);
    230             mDeletePolicyView.setAdapter(deletePoliciesAdapter);
    231         }
    232 
    233         // Set up security type spinner
    234         ArrayList<SpinnerOption> securityTypes = new ArrayList<SpinnerOption>();
    235         securityTypes.add(
    236                 new SpinnerOption(HostAuth.FLAG_NONE, context.getString(
    237                         R.string.account_setup_incoming_security_none_label)));
    238         securityTypes.add(
    239                 new SpinnerOption(HostAuth.FLAG_SSL, context.getString(
    240                         R.string.account_setup_incoming_security_ssl_label)));
    241         securityTypes.add(
    242                 new SpinnerOption(HostAuth.FLAG_SSL | HostAuth.FLAG_TRUST_ALL, context.getString(
    243                         R.string.account_setup_incoming_security_ssl_trust_certificates_label)));
    244         if (mServiceInfo.offerTls) {
    245             securityTypes.add(
    246                     new SpinnerOption(HostAuth.FLAG_TLS, context.getString(
    247                             R.string.account_setup_incoming_security_tls_label)));
    248             securityTypes.add(new SpinnerOption(HostAuth.FLAG_TLS | HostAuth.FLAG_TRUST_ALL,
    249                     context.getString(R.string
    250                             .account_setup_incoming_security_tls_trust_certificates_label)));
    251         }
    252         ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(
    253                 context, android.R.layout.simple_spinner_item, securityTypes);
    254         securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
    255         mSecurityTypeView.setAdapter(securityTypesAdapter);
    256 
    257         configureEditor();
    258         loadSettings();
    259 
    260         final List<VendorPolicyLoader.OAuthProvider> oauthProviders =
    261                 AccountSettingsUtils.getAllOAuthProviders(getActivity());
    262         final boolean offerOAuth = (mServiceInfo.offerOAuth && oauthProviders.size() > 0);
    263         if (mAuthenticationLabel != null) {
    264             if (offerOAuth) {
    265                 mAuthenticationLabel.setText(R.string.authentication_label);
    266             } else {
    267                 mAuthenticationLabel.setText(R.string.account_setup_basics_password_label);
    268             }
    269         }
    270     }
    271 
    272     /**
    273      * Called when the fragment is visible to the user and actively running.
    274      */
    275     @Override
    276     public void onResume() {
    277         super.onResume();
    278         validateFields();
    279     }
    280 
    281     @Override
    282     public void onDestroyView() {
    283         // Make sure we don't get callbacks after the views are supposed to be destroyed
    284         // and also don't hold onto them longer than we need
    285         if (mUsernameView != null) {
    286             mUsernameView.removeTextChangedListener(mValidationTextWatcher);
    287         }
    288         mUsernameView = null;
    289         mServerLabelView = null;
    290         if (mServerView != null) {
    291             mServerView.removeTextChangedListener(mValidationTextWatcher);
    292         }
    293         mServerView = null;
    294         if (mPortView != null) {
    295             mPortView.removeTextChangedListener(mValidationTextWatcher);
    296         }
    297         mPortView = null;
    298         if (mSecurityTypeView != null) {
    299             mSecurityTypeView.setOnItemSelectedListener(null);
    300         }
    301         mSecurityTypeView = null;
    302         mDeletePolicyLabelView = null;
    303         mDeletePolicyView = null;
    304         mImapPathPrefixSectionView = null;
    305         mImapPathPrefixView = null;
    306         mDeviceIdSection = null;
    307         mClientCertificateSelector = null;
    308 
    309         super.onDestroyView();
    310     }
    311 
    312     @Override
    313     public void onSaveInstanceState(Bundle outState) {
    314         super.onSaveInstanceState(outState);
    315 
    316         outState.putString(STATE_KEY_CREDENTIAL, mCacheLoginCredential);
    317         outState.putBoolean(STATE_KEY_LOADED, mLoaded);
    318     }
    319 
    320     /**
    321      * Configure the editor for the account type
    322      */
    323     private void configureEditor() {
    324         final Account account = mSetupData.getAccount();
    325         if (account == null || account.mHostAuthRecv == null) {
    326             LogUtils.e(LogUtils.TAG,
    327                     "null account or host auth. account null: %b host auth null: %b",
    328                     account == null, account == null || account.mHostAuthRecv == null);
    329             return;
    330         }
    331         mBaseScheme = account.mHostAuthRecv.mProtocol;
    332         mServerLabelView.setText(R.string.account_setup_incoming_server_label);
    333         mServerView.setContentDescription(getResources().getText(
    334                 R.string.account_setup_incoming_server_label));
    335         if (!mServiceInfo.offerPrefix) {
    336             mImapPathPrefixSectionView.setVisibility(View.GONE);
    337         }
    338         if (!mServiceInfo.offerLocalDeletes) {
    339             mDeletePolicyLabelView.setVisibility(View.GONE);
    340             mDeletePolicyView.setVisibility(View.GONE);
    341             mPortView.setImeOptions(EditorInfo.IME_ACTION_NEXT);
    342         }
    343     }
    344 
    345     /**
    346      * Load the current settings into the UI
    347      */
    348     private void loadSettings() {
    349         if (mLoaded) return;
    350 
    351         final Account account = mSetupData.getAccount();
    352         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
    353         mServiceInfo = mSetupData.getIncomingServiceInfo(getActivity());
    354         final List<VendorPolicyLoader.OAuthProvider> oauthProviders =
    355                 AccountSettingsUtils.getAllOAuthProviders(getActivity());
    356 
    357         final boolean offerOAuth = (mServiceInfo.offerOAuth && oauthProviders.size() > 0);
    358         mAuthenticationView.setAuthInfo(offerOAuth, recvAuth);
    359 
    360         final String username = recvAuth.mLogin;
    361         if (username != null) {
    362             //*** For eas?
    363             // Add a backslash to the start of the username, but only if the username has no
    364             // backslash in it.
    365             //if (userName.indexOf('\\') < 0) {
    366             //    userName = "\\" + userName;
    367             //}
    368             mUsernameView.setText(username);
    369         }
    370 
    371         if (mServiceInfo.offerPrefix) {
    372             final String prefix = recvAuth.mDomain;
    373             if (prefix != null && prefix.length() > 0) {
    374                 mImapPathPrefixView.setText(prefix.substring(1));
    375             }
    376         }
    377 
    378         // The delete policy is set for all legacy accounts. For POP3 accounts, the user sets
    379         // the policy explicitly. For IMAP accounts, the policy is set when the Account object
    380         // is created. @see AccountSetupBasics#populateSetupData
    381         mLoadedDeletePolicy = account.getDeletePolicy();
    382         SpinnerOption.setSpinnerOptionValue(mDeletePolicyView, mLoadedDeletePolicy);
    383 
    384         int flags = recvAuth.mFlags;
    385         if (mServiceInfo.defaultSsl) {
    386             flags |= HostAuth.FLAG_SSL;
    387         }
    388         // Strip out any flags that are not related to security type.
    389         int securityTypeFlags = (flags & HostAuth.FLAG_TRANSPORTSECURITY_MASK);
    390         SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, securityTypeFlags);
    391 
    392         final String hostname = recvAuth.mAddress;
    393         if (hostname != null) {
    394             mServerView.setText(hostname);
    395         }
    396 
    397         final int port = recvAuth.mPort;
    398         if (port != HostAuth.PORT_UNKNOWN) {
    399             mPortView.setText(Integer.toString(port));
    400         } else {
    401             updatePortFromSecurityType();
    402         }
    403 
    404         if (!TextUtils.isEmpty(recvAuth.mClientCertAlias)) {
    405             mClientCertificateSelector.setCertificate(recvAuth.mClientCertAlias);
    406         }
    407 
    408         // Make a deep copy of the HostAuth to compare with later
    409         final Parcel parcel = Parcel.obtain();
    410         parcel.writeParcelable(recvAuth, recvAuth.describeContents());
    411         parcel.setDataPosition(0);
    412         mLoadedRecvAuth = parcel.readParcelable(HostAuth.class.getClassLoader());
    413         parcel.recycle();
    414 
    415         mLoaded = true;
    416         validateFields();
    417     }
    418 
    419     /**
    420      * Check the values in the fields and decide if it makes sense to enable the "next" button
    421      */
    422     private void validateFields() {
    423         if (!mLoaded) return;
    424         enableNextButton(!TextUtils.isEmpty(mUsernameView.getText())
    425                 && mAuthenticationView.getAuthValid()
    426                 && Utility.isServerNameValid(mServerView)
    427                 && Utility.isPortFieldValid(mPortView));
    428 
    429         mCacheLoginCredential = mUsernameView.getText().toString().trim();
    430     }
    431 
    432     private int getPortFromSecurityType(boolean useSsl) {
    433         return useSsl ? mServiceInfo.portSsl : mServiceInfo.port;
    434     }
    435 
    436     private boolean getSslSelected() {
    437         final int securityType =
    438                 (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
    439         return ((securityType & HostAuth.FLAG_SSL) != 0);
    440     }
    441 
    442     public void onUseSslChanged(boolean useSsl) {
    443         if (mServiceInfo.offerCerts) {
    444             final int mode = useSsl ? View.VISIBLE : View.GONE;
    445             mClientCertificateSelector.setVisibility(mode);
    446             String deviceId = "";
    447             try {
    448                 deviceId = Device.getDeviceId(mAppContext);
    449             } catch (IOException e) {
    450                 // Not required
    451             }
    452             ((TextView) UiUtilities.getView(getView(), R.id.device_id)).setText(deviceId);
    453 
    454             mDeviceIdSection.setVisibility(mode);
    455         }
    456     }
    457 
    458     private void updatePortFromSecurityType() {
    459         final boolean sslSelected = getSslSelected();
    460         final int port = getPortFromSecurityType(sslSelected);
    461         mPortView.setText(Integer.toString(port));
    462         onUseSslChanged(sslSelected);
    463     }
    464 
    465     @Override
    466     public void saveSettings() {
    467         // Reset this here so we don't get stuck on this screen
    468         mLoadedDeletePolicy = mSetupData.getAccount().getDeletePolicy();
    469         super.saveSettings();
    470     }
    471 
    472     private static class SaveSettingsLoader extends MailAsyncTaskLoader<Boolean> {
    473         private final SetupDataFragment mSetupData;
    474         private final boolean mSettingsMode;
    475 
    476         private SaveSettingsLoader(Context context, SetupDataFragment setupData,
    477                 boolean settingsMode) {
    478             super(context);
    479             mSetupData = setupData;
    480             mSettingsMode = settingsMode;
    481         }
    482 
    483         @Override
    484         public Boolean loadInBackground() {
    485             if (mSettingsMode) {
    486                 saveSettingsAfterEdit(getContext(), mSetupData);
    487             } else {
    488                 saveSettingsAfterSetup(getContext(), mSetupData);
    489             }
    490             return true;
    491         }
    492 
    493         @Override
    494         protected void onDiscardResult(Boolean result) {}
    495     }
    496 
    497     @Override
    498     public Loader<Boolean> getSaveSettingsLoader() {
    499         return new SaveSettingsLoader(mAppContext, mSetupData, mSettingsMode);
    500     }
    501 
    502     /**
    503      * Entry point from Activity after editing settings and verifying them.  Must be FLOW_MODE_EDIT.
    504      * Note, we update account here (as well as the account.mHostAuthRecv) because we edit
    505      * account's delete policy here.
    506      * Blocking - do not call from UI Thread.
    507      */
    508     public static void saveSettingsAfterEdit(Context context, SetupDataFragment setupData) {
    509         final Account account = setupData.getAccount();
    510         account.update(context, account.toContentValues());
    511         final Credential cred = account.mHostAuthRecv.mCredential;
    512         if (cred != null) {
    513             if (cred.isSaved()) {
    514                 cred.update(context, cred.toContentValues());
    515             } else {
    516                 cred.save(context);
    517                 account.mHostAuthRecv.mCredentialKey = cred.mId;
    518             }
    519         }
    520         account.mHostAuthRecv.update(context, account.mHostAuthRecv.toContentValues());
    521         // Update the backup (side copy) of the accounts
    522         AccountBackupRestore.backup(context);
    523     }
    524 
    525     /**
    526      * Entry point from Activity after entering new settings and verifying them.  For setup mode.
    527      */
    528     public static void saveSettingsAfterSetup(Context context, SetupDataFragment setupData) {
    529         final Account account = setupData.getAccount();
    530         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
    531         final HostAuth sendAuth = account.getOrCreateHostAuthSend(context);
    532 
    533         // Set the username and password for the outgoing settings to the username and
    534         // password the user just set for incoming.  Use the verified host address to try and
    535         // pick a smarter outgoing address.
    536         final String hostName =
    537                 AccountSettingsUtils.inferServerName(context, recvAuth.mAddress, null, "smtp");
    538         sendAuth.setLogin(recvAuth.mLogin, recvAuth.mPassword);
    539         sendAuth.setConnection(sendAuth.mProtocol, hostName, sendAuth.mPort, sendAuth.mFlags);
    540     }
    541 
    542     /**
    543      * Entry point from Activity, when "next" button is clicked
    544      */
    545     @Override
    546     public int collectUserInputInternal() {
    547         final Account account = mSetupData.getAccount();
    548 
    549         // Make sure delete policy is an valid option before using it; otherwise, the results are
    550         // indeterminate, I suspect...
    551         if (mDeletePolicyView.getVisibility() == View.VISIBLE) {
    552             account.setDeletePolicy(
    553                     (Integer) ((SpinnerOption) mDeletePolicyView.getSelectedItem()).value);
    554         }
    555 
    556         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
    557         final String userName = mUsernameView.getText().toString().trim();
    558         final String userPassword = mAuthenticationView.getPassword();
    559         recvAuth.setLogin(userName, userPassword);
    560         if (!TextUtils.isEmpty(mAuthenticationView.getOAuthProvider())) {
    561             Credential cred = recvAuth.getOrCreateCredential(getActivity());
    562             cred.mProviderId = mAuthenticationView.getOAuthProvider();
    563         }
    564 
    565         final String serverAddress = mServerView.getText().toString().trim();
    566         int serverPort;
    567         try {
    568             serverPort = Integer.parseInt(mPortView.getText().toString().trim());
    569         } catch (NumberFormatException e) {
    570             serverPort = getPortFromSecurityType(getSslSelected());
    571             LogUtils.d(LogUtils.TAG, "Non-integer server port; using '" + serverPort + "'");
    572         }
    573         final int securityType =
    574                 (Integer) ((SpinnerOption) mSecurityTypeView.getSelectedItem()).value;
    575         recvAuth.setConnection(mBaseScheme, serverAddress, serverPort, securityType);
    576         if (mServiceInfo.offerPrefix) {
    577             final String prefix = mImapPathPrefixView.getText().toString().trim();
    578             recvAuth.mDomain = TextUtils.isEmpty(prefix) ? null : ("/" + prefix);
    579         } else {
    580             recvAuth.mDomain = null;
    581         }
    582         recvAuth.mClientCertAlias = mClientCertificateSelector.getCertificate();
    583 
    584         return SetupDataFragment.CHECK_INCOMING;
    585     }
    586 
    587     @Override
    588     public boolean haveSettingsChanged() {
    589         final boolean deletePolicyChanged;
    590 
    591         // Only verify the delete policy if the control is visible (i.e. is a pop3 account)
    592         if (mDeletePolicyView != null && mDeletePolicyView.getVisibility() == View.VISIBLE) {
    593             int newDeletePolicy =
    594                 (Integer)((SpinnerOption)mDeletePolicyView.getSelectedItem()).value;
    595             deletePolicyChanged = mLoadedDeletePolicy != newDeletePolicy;
    596         } else {
    597             deletePolicyChanged = false;
    598         }
    599 
    600         return deletePolicyChanged || super.haveSettingsChanged();
    601     }
    602 
    603     @Override
    604     public void onValidateStateChanged() {
    605         validateFields();
    606     }
    607 
    608     @Override
    609     public void onRequestSignIn() {
    610         // Launch the credentials activity.
    611         final String protocol =
    612                 mSetupData.getAccount().getOrCreateHostAuthRecv(mAppContext).mProtocol;
    613         final Intent intent = AccountCredentials.getAccountCredentialsIntent(getActivity(),
    614                 mUsernameView.getText().toString(), protocol);
    615         startActivityForResult(intent, SIGN_IN_REQUEST);
    616     }
    617 
    618     @Override
    619     public void onCertificateRequested() {
    620         final Intent intent = new Intent(getString(R.string.intent_exchange_cert_action));
    621         intent.setData(CertificateRequestor.CERTIFICATE_REQUEST_URI);
    622         intent.putExtra(CertificateRequestor.EXTRA_HOST, mServerView.getText().toString().trim());
    623         try {
    624             intent.putExtra(CertificateRequestor.EXTRA_PORT,
    625                     Integer.parseInt(mPortView.getText().toString().trim()));
    626         } catch (final NumberFormatException e) {
    627             LogUtils.d(LogUtils.TAG, "Couldn't parse port %s", mPortView.getText());
    628         }
    629         startActivityForResult(intent, CERTIFICATE_REQUEST);
    630     }
    631 
    632     @Override
    633     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    634         if (requestCode == CERTIFICATE_REQUEST && resultCode == Activity.RESULT_OK) {
    635             final String certAlias = data.getStringExtra(CertificateRequestor.RESULT_ALIAS);
    636             if (certAlias != null) {
    637                 mClientCertificateSelector.setCertificate(certAlias);
    638             }
    639         } else if (requestCode == SIGN_IN_REQUEST && resultCode == Activity.RESULT_OK) {
    640             final Account account = mSetupData.getAccount();
    641             final HostAuth recvAuth = account.getOrCreateHostAuthRecv(getActivity());
    642             AccountSetupCredentialsFragment.populateHostAuthWithResults(mAppContext, recvAuth,
    643                     data.getExtras());
    644             mAuthenticationView.setAuthInfo(mServiceInfo.offerOAuth, recvAuth);
    645         }
    646     }
    647 }
    648