AccountSetupIncomingFragment.java revision b63b39d03e2b1f5ea3998459d1ba96124316d907
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
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 (recvAuth.mClientCertAlias != null) {
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