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
17package com.android.email.activity.setup;
18
19import android.app.Activity;
20import android.content.Context;
21import android.content.Intent;
22import android.content.Loader;
23import android.os.Bundle;
24import android.os.Parcel;
25import android.text.Editable;
26import android.text.TextUtils;
27import android.text.TextWatcher;
28import android.text.method.DigitsKeyListener;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.inputmethod.EditorInfo;
33import android.widget.AdapterView;
34import android.widget.ArrayAdapter;
35import android.widget.EditText;
36import android.widget.Spinner;
37import android.widget.TextView;
38
39import com.android.email.R;
40import com.android.email.activity.UiUtilities;
41import com.android.email.activity.setup.AuthenticationView.AuthenticationCallback;
42import com.android.email.provider.AccountBackupRestore;
43import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
44import com.android.email.view.CertificateSelector;
45import com.android.email.view.CertificateSelector.HostCallback;
46import com.android.emailcommon.Device;
47import com.android.emailcommon.VendorPolicyLoader;
48import com.android.emailcommon.provider.Account;
49import com.android.emailcommon.provider.Credential;
50import com.android.emailcommon.provider.HostAuth;
51import com.android.emailcommon.utility.CertificateRequestor;
52import com.android.emailcommon.utility.Utility;
53import com.android.mail.ui.MailAsyncTaskLoader;
54import com.android.mail.utils.LogUtils;
55
56import java.io.IOException;
57import java.util.ArrayList;
58import 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 */
66public 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