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 
    261     /**
    262      * Called when the fragment is visible to the user and actively running.
    263      */
    264     @Override
    265     public void onResume() {
    266         super.onResume();
    267         validateFields();
    268     }
    269 
    270     @Override
    271     public void onDestroyView() {
    272         // Make sure we don't get callbacks after the views are supposed to be destroyed
    273         // and also don't hold onto them longer than we need
    274         if (mUsernameView != null) {
    275             mUsernameView.removeTextChangedListener(mValidationTextWatcher);
    276         }
    277         mUsernameView = null;
    278         mServerLabelView = null;
    279         if (mServerView != null) {
    280             mServerView.removeTextChangedListener(mValidationTextWatcher);
    281         }
    282         mServerView = null;
    283         if (mPortView != null) {
    284             mPortView.removeTextChangedListener(mValidationTextWatcher);
    285         }
    286         mPortView = null;
    287         if (mSecurityTypeView != null) {
    288             mSecurityTypeView.setOnItemSelectedListener(null);
    289         }
    290         mSecurityTypeView = null;
    291         mDeletePolicyLabelView = null;
    292         mDeletePolicyView = null;
    293         mImapPathPrefixSectionView = null;
    294         mImapPathPrefixView = null;
    295         mDeviceIdSection = null;
    296         mClientCertificateSelector = null;
    297 
    298         super.onDestroyView();
    299     }
    300 
    301     @Override
    302     public void onSaveInstanceState(Bundle outState) {
    303         super.onSaveInstanceState(outState);
    304 
    305         outState.putString(STATE_KEY_CREDENTIAL, mCacheLoginCredential);
    306         outState.putBoolean(STATE_KEY_LOADED, mLoaded);
    307     }
    308 
    309     /**
    310      * Configure the editor for the account type
    311      */
    312     private void configureEditor() {
    313         final Account account = mSetupData.getAccount();
    314         if (account == null || account.mHostAuthRecv == null) {
    315             LogUtils.e(LogUtils.TAG,
    316                     "null account or host auth. account null: %b host auth null: %b",
    317                     account == null, account == null || account.mHostAuthRecv == null);
    318             return;
    319         }
    320         mBaseScheme = account.mHostAuthRecv.mProtocol;
    321         mServerLabelView.setText(R.string.account_setup_incoming_server_label);
    322         mServerView.setContentDescription(getResources().getText(
    323                 R.string.account_setup_incoming_server_label));
    324         if (!mServiceInfo.offerPrefix) {
    325             mImapPathPrefixSectionView.setVisibility(View.GONE);
    326         }
    327         if (!mServiceInfo.offerLocalDeletes) {
    328             mDeletePolicyLabelView.setVisibility(View.GONE);
    329             mDeletePolicyView.setVisibility(View.GONE);
    330             mPortView.setImeOptions(EditorInfo.IME_ACTION_NEXT);
    331         }
    332     }
    333 
    334     /**
    335      * Load the current settings into the UI
    336      */
    337     private void loadSettings() {
    338         if (mLoaded) return;
    339 
    340         final Account account = mSetupData.getAccount();
    341         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
    342         mServiceInfo = mSetupData.getIncomingServiceInfo(getActivity());
    343         final List<VendorPolicyLoader.OAuthProvider> oauthProviders =
    344                 AccountSettingsUtils.getAllOAuthProviders(getActivity());
    345         final boolean offerOAuth = (mServiceInfo.offerOAuth && oauthProviders.size() > 0);
    346 
    347         mAuthenticationView.setAuthInfo(offerOAuth, recvAuth);
    348         if (mAuthenticationLabel != null) {
    349             if (offerOAuth) {
    350                 mAuthenticationLabel.setText(R.string.authentication_label);
    351             } else {
    352                 mAuthenticationLabel.setText(R.string.account_setup_basics_password_label);
    353             }
    354         }
    355 
    356         final String username = recvAuth.mLogin;
    357         if (username != null) {
    358             //*** For eas?
    359             // Add a backslash to the start of the username, but only if the username has no
    360             // backslash in it.
    361             //if (userName.indexOf('\\') < 0) {
    362             //    userName = "\\" + userName;
    363             //}
    364             mUsernameView.setText(username);
    365         }
    366 
    367         if (mServiceInfo.offerPrefix) {
    368             final String prefix = recvAuth.mDomain;
    369             if (prefix != null && prefix.length() > 0) {
    370                 mImapPathPrefixView.setText(prefix.substring(1));
    371             }
    372         }
    373 
    374         // The delete policy is set for all legacy accounts. For POP3 accounts, the user sets
    375         // the policy explicitly. For IMAP accounts, the policy is set when the Account object
    376         // is created. @see AccountSetupBasics#populateSetupData
    377         mLoadedDeletePolicy = account.getDeletePolicy();
    378         SpinnerOption.setSpinnerOptionValue(mDeletePolicyView, mLoadedDeletePolicy);
    379 
    380         int flags = recvAuth.mFlags;
    381         if (mServiceInfo.defaultSsl) {
    382             flags |= HostAuth.FLAG_SSL;
    383         }
    384         // Strip out any flags that are not related to security type.
    385         int securityTypeFlags = (flags & HostAuth.FLAG_TRANSPORTSECURITY_MASK);
    386         SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, securityTypeFlags);
    387 
    388         final String hostname = recvAuth.mAddress;
    389         if (hostname != null) {
    390             mServerView.setText(hostname);
    391         }
    392 
    393         final int port = recvAuth.mPort;
    394         if (port != HostAuth.PORT_UNKNOWN) {
    395             mPortView.setText(Integer.toString(port));
    396         } else {
    397             updatePortFromSecurityType();
    398         }
    399 
    400         if (!TextUtils.isEmpty(recvAuth.mClientCertAlias)) {
    401             mClientCertificateSelector.setCertificate(recvAuth.mClientCertAlias);
    402         }
    403 
    404         // Make a deep copy of the HostAuth to compare with later
    405         final Parcel parcel = Parcel.obtain();
    406         parcel.writeParcelable(recvAuth, recvAuth.describeContents());
    407         parcel.setDataPosition(0);
    408         mLoadedRecvAuth = parcel.readParcelable(HostAuth.class.getClassLoader());
    409         parcel.recycle();
    410 
    411         mLoaded = true;
    412         validateFields();
    413     }
    414 
    415     /**
    416      * Check the values in the fields and decide if it makes sense to enable the "next" button
    417      */
    418     private void validateFields() {
    419         if (!mLoaded) return;
    420         enableNextButton(!TextUtils.isEmpty(mUsernameView.getText())
    421                 && mAuthenticationView.getAuthValid()
    422                 && Utility.isServerNameValid(mServerView)
    423                 && Utility.isPortFieldValid(mPortView));
    424 
    425         mCacheLoginCredential = mUsernameView.getText().toString().trim();
    426     }
    427 
    428     private int getPortFromSecurityType(boolean useSsl) {
    429         return useSsl ? mServiceInfo.portSsl : mServiceInfo.port;
    430     }
    431 
    432     private boolean getSslSelected() {
    433         final int securityType =
    434                 (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
    435         return ((securityType & HostAuth.FLAG_SSL) != 0);
    436     }
    437 
    438     public void onUseSslChanged(boolean useSsl) {
    439         if (mServiceInfo.offerCerts) {
    440             final int mode = useSsl ? View.VISIBLE : View.GONE;
    441             mClientCertificateSelector.setVisibility(mode);
    442             String deviceId = "";
    443             try {
    444                 deviceId = Device.getDeviceId(mAppContext);
    445             } catch (IOException e) {
    446                 // Not required
    447             }
    448             ((TextView) UiUtilities.getView(getView(), R.id.device_id)).setText(deviceId);
    449 
    450             mDeviceIdSection.setVisibility(mode);
    451         }
    452     }
    453 
    454     private void updatePortFromSecurityType() {
    455         final boolean sslSelected = getSslSelected();
    456         final int port = getPortFromSecurityType(sslSelected);
    457         mPortView.setText(Integer.toString(port));
    458         onUseSslChanged(sslSelected);
    459     }
    460 
    461     @Override
    462     public void saveSettings() {
    463         // Reset this here so we don't get stuck on this screen
    464         mLoadedDeletePolicy = mSetupData.getAccount().getDeletePolicy();
    465         super.saveSettings();
    466     }
    467 
    468     private static class SaveSettingsLoader extends MailAsyncTaskLoader<Boolean> {
    469         private final SetupDataFragment mSetupData;
    470         private final boolean mSettingsMode;
    471 
    472         private SaveSettingsLoader(Context context, SetupDataFragment setupData,
    473                 boolean settingsMode) {
    474             super(context);
    475             mSetupData = setupData;
    476             mSettingsMode = settingsMode;
    477         }
    478 
    479         @Override
    480         public Boolean loadInBackground() {
    481             if (mSettingsMode) {
    482                 saveSettingsAfterEdit(getContext(), mSetupData);
    483             } else {
    484                 saveSettingsAfterSetup(getContext(), mSetupData);
    485             }
    486             return true;
    487         }
    488 
    489         @Override
    490         protected void onDiscardResult(Boolean result) {}
    491     }
    492 
    493     @Override
    494     public Loader<Boolean> getSaveSettingsLoader() {
    495         return new SaveSettingsLoader(mAppContext, mSetupData, mSettingsMode);
    496     }
    497 
    498     /**
    499      * Entry point from Activity after editing settings and verifying them.  Must be FLOW_MODE_EDIT.
    500      * Note, we update account here (as well as the account.mHostAuthRecv) because we edit
    501      * account's delete policy here.
    502      * Blocking - do not call from UI Thread.
    503      */
    504     public static void saveSettingsAfterEdit(Context context, SetupDataFragment setupData) {
    505         final Account account = setupData.getAccount();
    506         account.update(context, account.toContentValues());
    507         final Credential cred = account.mHostAuthRecv.mCredential;
    508         if (cred != null) {
    509             if (cred.isSaved()) {
    510                 cred.update(context, cred.toContentValues());
    511             } else {
    512                 cred.save(context);
    513                 account.mHostAuthRecv.mCredentialKey = cred.mId;
    514             }
    515         }
    516         account.mHostAuthRecv.update(context, account.mHostAuthRecv.toContentValues());
    517         // Update the backup (side copy) of the accounts
    518         AccountBackupRestore.backup(context);
    519     }
    520 
    521     /**
    522      * Entry point from Activity after entering new settings and verifying them.  For setup mode.
    523      */
    524     public static void saveSettingsAfterSetup(Context context, SetupDataFragment setupData) {
    525         final Account account = setupData.getAccount();
    526         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(context);
    527         final HostAuth sendAuth = account.getOrCreateHostAuthSend(context);
    528 
    529         // Set the username and password for the outgoing settings to the username and
    530         // password the user just set for incoming.  Use the verified host address to try and
    531         // pick a smarter outgoing address.
    532         final String hostName =
    533                 AccountSettingsUtils.inferServerName(context, recvAuth.mAddress, null, "smtp");
    534         sendAuth.setLogin(recvAuth.mLogin, recvAuth.mPassword);
    535         sendAuth.setConnection(sendAuth.mProtocol, hostName, sendAuth.mPort, sendAuth.mFlags);
    536     }
    537 
    538     /**
    539      * Entry point from Activity, when "next" button is clicked
    540      */
    541     @Override
    542     public int collectUserInputInternal() {
    543         final Account account = mSetupData.getAccount();
    544 
    545         // Make sure delete policy is an valid option before using it; otherwise, the results are
    546         // indeterminate, I suspect...
    547         if (mDeletePolicyView.getVisibility() == View.VISIBLE) {
    548             account.setDeletePolicy(
    549                     (Integer) ((SpinnerOption) mDeletePolicyView.getSelectedItem()).value);
    550         }
    551 
    552         final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mAppContext);
    553         final String userName = mUsernameView.getText().toString().trim();
    554         final String userPassword = mAuthenticationView.getPassword();
    555         recvAuth.setLogin(userName, userPassword);
    556         if (!TextUtils.isEmpty(mAuthenticationView.getOAuthProvider())) {
    557             Credential cred = recvAuth.getOrCreateCredential(getActivity());
    558             cred.mProviderId = mAuthenticationView.getOAuthProvider();
    559         }
    560 
    561         final String serverAddress = mServerView.getText().toString().trim();
    562         int serverPort;
    563         try {
    564             serverPort = Integer.parseInt(mPortView.getText().toString().trim());
    565         } catch (NumberFormatException e) {
    566             serverPort = getPortFromSecurityType(getSslSelected());
    567             LogUtils.d(LogUtils.TAG, "Non-integer server port; using '" + serverPort + "'");
    568         }
    569         final int securityType =
    570                 (Integer) ((SpinnerOption) mSecurityTypeView.getSelectedItem()).value;
    571         recvAuth.setConnection(mBaseScheme, serverAddress, serverPort, securityType);
    572         if (mServiceInfo.offerPrefix) {
    573             final String prefix = mImapPathPrefixView.getText().toString().trim();
    574             recvAuth.mDomain = TextUtils.isEmpty(prefix) ? null : ("/" + prefix);
    575         } else {
    576             recvAuth.mDomain = null;
    577         }
    578         recvAuth.mClientCertAlias = mClientCertificateSelector.getCertificate();
    579 
    580         return SetupDataFragment.CHECK_INCOMING;
    581     }
    582 
    583     @Override
    584     public boolean haveSettingsChanged() {
    585         final boolean deletePolicyChanged;
    586 
    587         // Only verify the delete policy if the control is visible (i.e. is a pop3 account)
    588         if (mDeletePolicyView != null && mDeletePolicyView.getVisibility() == View.VISIBLE) {
    589             int newDeletePolicy =
    590                 (Integer)((SpinnerOption)mDeletePolicyView.getSelectedItem()).value;
    591             deletePolicyChanged = mLoadedDeletePolicy != newDeletePolicy;
    592         } else {
    593             deletePolicyChanged = false;
    594         }
    595 
    596         return deletePolicyChanged || super.haveSettingsChanged();
    597     }
    598 
    599     @Override
    600     public void onValidateStateChanged() {
    601         validateFields();
    602     }
    603 
    604     @Override
    605     public void onRequestSignIn() {
    606         // Launch the credentials activity.
    607         final String protocol =
    608                 mSetupData.getAccount().getOrCreateHostAuthRecv(mAppContext).mProtocol;
    609         final Intent intent = AccountCredentials.getAccountCredentialsIntent(getActivity(),
    610                 mUsernameView.getText().toString(), protocol);
    611         startActivityForResult(intent, SIGN_IN_REQUEST);
    612     }
    613 
    614     @Override
    615     public void onCertificateRequested() {
    616         final Intent intent = new Intent(getString(R.string.intent_exchange_cert_action));
    617         intent.setData(CertificateRequestor.CERTIFICATE_REQUEST_URI);
    618         intent.putExtra(CertificateRequestor.EXTRA_HOST, mServerView.getText().toString().trim());
    619         try {
    620             intent.putExtra(CertificateRequestor.EXTRA_PORT,
    621                     Integer.parseInt(mPortView.getText().toString().trim()));
    622         } catch (final NumberFormatException e) {
    623             LogUtils.d(LogUtils.TAG, "Couldn't parse port %s", mPortView.getText());
    624         }
    625         startActivityForResult(intent, CERTIFICATE_REQUEST);
    626     }
    627 
    628     @Override
    629     public void onActivityResult(int requestCode, int resultCode, Intent data) {
    630         if (requestCode == CERTIFICATE_REQUEST && resultCode == Activity.RESULT_OK) {
    631             final String certAlias = data.getStringExtra(CertificateRequestor.RESULT_ALIAS);
    632             if (certAlias != null) {
    633                 mClientCertificateSelector.setCertificate(certAlias);
    634             }
    635         } else if (requestCode == SIGN_IN_REQUEST && resultCode == Activity.RESULT_OK) {
    636             final Account account = mSetupData.getAccount();
    637             final HostAuth recvAuth = account.getOrCreateHostAuthRecv(getActivity());
    638             AccountSetupCredentialsFragment.populateHostAuthWithResults(mAppContext, recvAuth,
    639                     data.getExtras());
    640             mAuthenticationView.setAuthInfo(mServiceInfo.offerOAuth, recvAuth);
    641         }
    642     }
    643 }
    644