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 
newInstance(boolean settingsMode)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
AccountSetupIncomingFragment()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
onCreate(Bundle savedInstanceState)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
onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)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
onActivityCreated(Bundle savedInstanceState)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
onResume()276     public void onResume() {
277         super.onResume();
278         validateFields();
279     }
280 
281     @Override
onDestroyView()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
onSaveInstanceState(Bundle outState)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      */
configureEditor()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      */
loadSettings()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      */
validateFields()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 
getPortFromSecurityType(boolean useSsl)432     private int getPortFromSecurityType(boolean useSsl) {
433         return useSsl ? mServiceInfo.portSsl : mServiceInfo.port;
434     }
435 
getSslSelected()436     private boolean getSslSelected() {
437         final int securityType =
438                 (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
439         return ((securityType & HostAuth.FLAG_SSL) != 0);
440     }
441 
onUseSslChanged(boolean useSsl)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 
updatePortFromSecurityType()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
saveSettings()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 
SaveSettingsLoader(Context context, SetupDataFragment setupData, boolean settingsMode)476         private SaveSettingsLoader(Context context, SetupDataFragment setupData,
477                 boolean settingsMode) {
478             super(context);
479             mSetupData = setupData;
480             mSettingsMode = settingsMode;
481         }
482 
483         @Override
loadInBackground()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
onDiscardResult(Boolean result)494         protected void onDiscardResult(Boolean result) {}
495     }
496 
497     @Override
getSaveSettingsLoader()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      */
saveSettingsAfterEdit(Context context, SetupDataFragment setupData)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      */
saveSettingsAfterSetup(Context context, SetupDataFragment setupData)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
collectUserInputInternal()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
haveSettingsChanged()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
onValidateStateChanged()604     public void onValidateStateChanged() {
605         validateFields();
606     }
607 
608     @Override
onRequestSignIn()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
onCertificateRequested()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
onActivityResult(int requestCode, int resultCode, Intent data)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