AccountSetupIncomingFragment.java revision 648a067056db91d4a1bfd3c4eac9eec3d4c6adb4
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.net.Uri;
23import android.os.Bundle;
24import android.text.Editable;
25import android.text.TextUtils;
26import android.text.TextWatcher;
27import android.text.method.DigitsKeyListener;
28import android.view.LayoutInflater;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.inputmethod.EditorInfo;
32import android.widget.AdapterView;
33import android.widget.ArrayAdapter;
34import android.widget.EditText;
35import android.widget.Spinner;
36import android.widget.TextView;
37
38import com.android.email.R;
39import com.android.email.activity.UiUtilities;
40import com.android.email.provider.AccountBackupRestore;
41import com.android.email.service.EmailServiceUtils;
42import com.android.email.service.EmailServiceUtils.EmailServiceInfo;
43import com.android.email.view.CertificateSelector;
44import com.android.email.view.CertificateSelector.HostCallback;
45import com.android.email2.ui.MailActivityEmail;
46import com.android.emailcommon.Device;
47import com.android.emailcommon.Logging;
48import com.android.emailcommon.provider.Account;
49import com.android.emailcommon.provider.HostAuth;
50import com.android.emailcommon.utility.CertificateRequestor;
51import com.android.emailcommon.utility.Utility;
52import com.android.mail.utils.LogUtils;
53
54import java.io.IOException;
55import java.util.ArrayList;
56
57/**
58 * Provides UI for IMAP/POP account settings.
59 *
60 * This fragment is used by AccountSetupIncoming (for creating accounts) and by AccountSettingsXL
61 * (for editing existing accounts).
62 */
63public class AccountSetupIncomingFragment extends AccountServerBaseFragment
64        implements HostCallback {
65
66    private static final int CERTIFICATE_REQUEST = 0;
67    private final static String STATE_KEY_CREDENTIAL = "AccountSetupIncomingFragment.credential";
68    private final static String STATE_KEY_LOADED = "AccountSetupIncomingFragment.loaded";
69
70    private EditText mUsernameView;
71    private EditText mPasswordView;
72    private TextView mServerLabelView;
73    private EditText mServerView;
74    private EditText mPortView;
75    private Spinner mSecurityTypeView;
76    private TextView mDeletePolicyLabelView;
77    private Spinner mDeletePolicyView;
78    private View mImapPathPrefixSectionView;
79    private View mDeviceIdSectionView;
80    private EditText mImapPathPrefixView;
81    private CertificateSelector mClientCertificateSelector;
82    // Delete policy as loaded from the device
83    private int mLoadedDeletePolicy;
84
85    private TextWatcher mValidationTextWatcher;
86
87    // Support for lifecycle
88    private boolean mStarted;
89    private boolean mLoaded;
90    private String mCacheLoginCredential;
91    private EmailServiceInfo mServiceInfo;
92
93    // Public no-args constructor needed for fragment re-instantiation
94    public AccountSetupIncomingFragment() {}
95
96    /**
97     * Called to do initial creation of a fragment.  This is called after
98     * {@link #onAttach(Activity)} and before {@link #onActivityCreated(Bundle)}.
99     */
100    @Override
101    public void onCreate(Bundle savedInstanceState) {
102        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
103            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onCreate");
104        }
105        super.onCreate(savedInstanceState);
106
107        if (savedInstanceState != null) {
108            mCacheLoginCredential = savedInstanceState.getString(STATE_KEY_CREDENTIAL);
109            mLoaded = savedInstanceState.getBoolean(STATE_KEY_LOADED, false);
110        }
111    }
112
113    @Override
114    public View onCreateView(LayoutInflater inflater, ViewGroup container,
115            Bundle savedInstanceState) {
116        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
117            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onCreateView");
118        }
119        final int layoutId = mSettingsMode
120                ? R.layout.account_settings_incoming_fragment
121                : R.layout.account_setup_incoming_fragment;
122
123        final View view = inflater.inflate(layoutId, container, false);
124
125        mUsernameView = UiUtilities.getView(view, R.id.account_username);
126        mPasswordView = UiUtilities.getView(view, R.id.account_password);
127        mServerLabelView = UiUtilities.getView(view, R.id.account_server_label);
128        mServerView = UiUtilities.getView(view, R.id.account_server);
129        mPortView = UiUtilities.getView(view, R.id.account_port);
130        mSecurityTypeView = UiUtilities.getView(view, R.id.account_security_type);
131        mDeletePolicyLabelView = UiUtilities.getView(view, R.id.account_delete_policy_label);
132        mDeletePolicyView = UiUtilities.getView(view, R.id.account_delete_policy);
133        mImapPathPrefixSectionView = UiUtilities.getView(view, R.id.imap_path_prefix_section);
134        mDeviceIdSectionView = UiUtilities.getView(view, R.id.device_id_section);
135        mImapPathPrefixView = UiUtilities.getView(view, R.id.imap_path_prefix);
136        mClientCertificateSelector = UiUtilities.getView(view, R.id.client_certificate_selector);
137
138        // Updates the port when the user changes the security type. This allows
139        // us to show a reasonable default which the user can change.
140        mSecurityTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
141            @Override
142            public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
143                updatePortFromSecurityType();
144            }
145
146            @Override
147            public void onNothingSelected(AdapterView<?> arg0) { }
148        });
149
150        // After any text edits, call validateFields() which enables or disables the Next button
151        mValidationTextWatcher = new TextWatcher() {
152            @Override
153            public void afterTextChanged(Editable s) {
154                validateFields();
155            }
156
157            @Override
158            public void beforeTextChanged(CharSequence s, int start, int count, int after) { }
159            @Override
160            public void onTextChanged(CharSequence s, int start, int before, int count) { }
161        };
162        // We're editing an existing account; don't allow modification of the user name
163        if (mSettingsMode) {
164            makeTextViewUneditable(mUsernameView,
165                    getString(R.string.account_setup_username_uneditable_error));
166        }
167        mUsernameView.addTextChangedListener(mValidationTextWatcher);
168        mPasswordView.addTextChangedListener(mValidationTextWatcher);
169        mServerView.addTextChangedListener(mValidationTextWatcher);
170        mPortView.addTextChangedListener(mValidationTextWatcher);
171
172        // Only allow digits in the port field.
173        mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
174
175        // Additional setup only used while in "settings" mode
176        onCreateViewSettingsMode(view);
177
178        return view;
179    }
180
181    @Override
182    public void onActivityCreated(Bundle savedInstanceState) {
183        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
184            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onActivityCreated");
185        }
186        super.onActivityCreated(savedInstanceState);
187        mClientCertificateSelector.setHostActivity(this);
188
189        final Context context = getActivity();
190        final SetupData.SetupDataContainer container = (SetupData.SetupDataContainer) context;
191        mSetupData = container.getSetupData();
192
193        final HostAuth recvAuth = mSetupData.getAccount().mHostAuthRecv;
194        mServiceInfo = EmailServiceUtils.getServiceInfo(mContext, recvAuth.mProtocol);
195
196        if (mServiceInfo.offerLocalDeletes) {
197            SpinnerOption deletePolicies[] = {
198                    new SpinnerOption(Account.DELETE_POLICY_NEVER,
199                            context.getString(
200                                    R.string.account_setup_incoming_delete_policy_never_label)),
201                    new SpinnerOption(Account.DELETE_POLICY_ON_DELETE,
202                            context.getString(
203                                    R.string.account_setup_incoming_delete_policy_delete_label)),
204            };
205            ArrayAdapter<SpinnerOption> deletePoliciesAdapter =
206                    new ArrayAdapter<SpinnerOption>(context,
207                            android.R.layout.simple_spinner_item, deletePolicies);
208            deletePoliciesAdapter.setDropDownViewResource(
209                    android.R.layout.simple_spinner_dropdown_item);
210            mDeletePolicyView.setAdapter(deletePoliciesAdapter);
211        }
212
213        // Set up security type spinner
214        ArrayList<SpinnerOption> securityTypes = new ArrayList<SpinnerOption>();
215        securityTypes.add(
216                new SpinnerOption(HostAuth.FLAG_NONE, context.getString(
217                        R.string.account_setup_incoming_security_none_label)));
218        securityTypes.add(
219                new SpinnerOption(HostAuth.FLAG_SSL, context.getString(
220                        R.string.account_setup_incoming_security_ssl_label)));
221        securityTypes.add(
222                new SpinnerOption(HostAuth.FLAG_SSL | HostAuth.FLAG_TRUST_ALL, context.getString(
223                        R.string.account_setup_incoming_security_ssl_trust_certificates_label)));
224        if (mServiceInfo.offerTls) {
225            securityTypes.add(
226                    new SpinnerOption(HostAuth.FLAG_TLS, context.getString(
227                            R.string.account_setup_incoming_security_tls_label)));
228            securityTypes.add(new SpinnerOption(HostAuth.FLAG_TLS | HostAuth.FLAG_TRUST_ALL,
229                    context.getString(R.string
230                            .account_setup_incoming_security_tls_trust_certificates_label)));
231        }
232        ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(
233                context, android.R.layout.simple_spinner_item, securityTypes);
234        securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
235        mSecurityTypeView.setAdapter(securityTypesAdapter);
236    }
237
238    /**
239     * Called when the Fragment is visible to the user.
240     */
241    @Override
242    public void onStart() {
243        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
244            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onStart");
245        }
246        super.onStart();
247        mStarted = true;
248        configureEditor();
249        loadSettings();
250    }
251
252    /**
253     * Called when the fragment is visible to the user and actively running.
254     */
255    @Override
256    public void onResume() {
257        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
258            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onResume");
259        }
260        super.onResume();
261        validateFields();
262    }
263
264    @Override
265    public void onPause() {
266        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
267            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onPause");
268        }
269        super.onPause();
270    }
271
272    /**
273     * Called when the Fragment is no longer started.
274     */
275    @Override
276    public void onStop() {
277        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
278            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onStop");
279        }
280        super.onStop();
281        mStarted = false;
282    }
283
284    @Override
285    public void onDestroyView() {
286        // Make sure we don't get callbacks after the views are supposed to be destroyed
287        // and also don't hold onto them longer than we need
288        if (mUsernameView != null) {
289            mUsernameView.removeTextChangedListener(mValidationTextWatcher);
290        }
291        mUsernameView = null;
292        if (mPasswordView != null) {
293            mPasswordView.removeTextChangedListener(mValidationTextWatcher);
294        }
295        mPasswordView = null;
296        mServerLabelView = null;
297        if (mServerView != null) {
298            mServerView.removeTextChangedListener(mValidationTextWatcher);
299        }
300        mServerView = null;
301        if (mPortView != null) {
302            mPortView.removeTextChangedListener(mValidationTextWatcher);
303        }
304        mPortView = null;
305        if (mSecurityTypeView != null) {
306            mSecurityTypeView.setOnItemSelectedListener(null);
307        }
308        mSecurityTypeView = null;
309        mDeletePolicyLabelView = null;
310        mDeletePolicyView = null;
311        mImapPathPrefixSectionView = null;
312        mDeviceIdSectionView = null;
313        mImapPathPrefixView = null;
314        mClientCertificateSelector = null;
315
316        super.onDestroyView();
317    }
318
319    /**
320     * Called when the fragment is no longer in use.
321     */
322    @Override
323    public void onDestroy() {
324        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
325            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onDestroy");
326        }
327        super.onDestroy();
328    }
329
330    @Override
331    public void onSaveInstanceState(Bundle outState) {
332        if (Logging.DEBUG_LIFECYCLE && MailActivityEmail.DEBUG) {
333            LogUtils.d(Logging.LOG_TAG, "AccountSetupIncomingFragment onSaveInstanceState");
334        }
335        super.onSaveInstanceState(outState);
336
337        outState.putString(STATE_KEY_CREDENTIAL, mCacheLoginCredential);
338        outState.putBoolean(STATE_KEY_LOADED, mLoaded);
339    }
340
341    /**
342     * Activity provides callbacks here.  This also triggers loading and setting up the UX
343     */
344    @Override
345    public void setCallback(Callback callback) {
346        super.setCallback(callback);
347        if (mStarted) {
348            configureEditor();
349            loadSettings();
350        }
351    }
352
353    /**
354     * Configure the editor for the account type
355     */
356    private void configureEditor() {
357        final Account account = mSetupData.getAccount();
358        if (account == null || account.mHostAuthRecv == null) {
359            LogUtils.e(Logging.LOG_TAG,
360                    "null account or host auth. account null: %b host auth null: %b",
361                    account == null, account == null || account.mHostAuthRecv == null);
362            return;
363        }
364        TextView lastView = mImapPathPrefixView;
365        mBaseScheme = account.mHostAuthRecv.mProtocol;
366        mServerLabelView.setText(R.string.account_setup_incoming_server_label);
367        mServerView.setContentDescription(getResources().getText(
368                R.string.account_setup_incoming_server_label));
369        if (!mServiceInfo.offerPrefix) {
370            mImapPathPrefixSectionView.setVisibility(View.GONE);
371            lastView = mPortView;
372        }
373        if (!mServiceInfo.offerLocalDeletes) {
374            mDeletePolicyLabelView.setVisibility(View.GONE);
375            mDeletePolicyView.setVisibility(View.GONE);
376            mPortView.setImeOptions(EditorInfo.IME_ACTION_NEXT);
377        }
378        lastView.setOnEditorActionListener(mDismissImeOnDoneListener);
379    }
380
381    /**
382     * Load the current settings into the UI
383     */
384    private void loadSettings() {
385        if (mLoaded) return;
386
387        final Account account = mSetupData.getAccount();
388        final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
389
390        final String username = recvAuth.mLogin;
391        if (username != null) {
392            //*** For eas?
393            // Add a backslash to the start of the username, but only if the username has no
394            // backslash in it.
395            //if (userName.indexOf('\\') < 0) {
396            //    userName = "\\" + userName;
397            //}
398            mUsernameView.setText(username);
399        }
400        final String password = recvAuth.mPassword;
401        if (password != null) {
402            mPasswordView.setText(password);
403            // Since username is uneditable, focus on the next editable field
404            if (mSettingsMode) {
405                mPasswordView.requestFocus();
406            }
407        }
408
409        if (mServiceInfo.offerPrefix) {
410            final String prefix = recvAuth.mDomain;
411            if (prefix != null && prefix.length() > 0) {
412                mImapPathPrefixView.setText(prefix.substring(1));
413            }
414        }
415
416        // The delete policy is set for all legacy accounts. For POP3 accounts, the user sets
417        // the policy explicitly. For IMAP accounts, the policy is set when the Account object
418        // is created. @see AccountSetupBasics#populateSetupData
419        mLoadedDeletePolicy = account.getDeletePolicy();
420        SpinnerOption.setSpinnerOptionValue(mDeletePolicyView, mLoadedDeletePolicy);
421
422        int flags = recvAuth.mFlags;
423        flags &= ~HostAuth.FLAG_AUTHENTICATE;
424        if (mServiceInfo.defaultSsl) {
425            flags |= HostAuth.FLAG_SSL;
426        }
427        SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, flags);
428
429        final String hostname = recvAuth.mAddress;
430        if (hostname != null) {
431            mServerView.setText(hostname);
432        }
433
434        final int port = recvAuth.mPort;
435        if (port != HostAuth.PORT_UNKNOWN) {
436            mPortView.setText(Integer.toString(port));
437        } else {
438            updatePortFromSecurityType();
439        }
440
441        mLoadedRecvAuth = recvAuth;
442        mLoaded = true;
443        validateFields();
444    }
445
446    /**
447     * Check the values in the fields and decide if it makes sense to enable the "next" button
448     */
449    private void validateFields() {
450        if (!mLoaded) return;
451        enableNextButton(!TextUtils.isEmpty(mUsernameView.getText())
452                && !TextUtils.isEmpty(mPasswordView.getText())
453                && Utility.isServerNameValid(mServerView)
454                && Utility.isPortFieldValid(mPortView));
455
456        mCacheLoginCredential = mUsernameView.getText().toString().trim();
457
458        // Warn (but don't prevent) if password has leading/trailing spaces
459        AccountSettingsUtils.checkPasswordSpaces(mContext, mPasswordView);
460    }
461
462    private int getPortFromSecurityType(boolean useSsl) {
463        final EmailServiceInfo info = EmailServiceUtils.getServiceInfo(mContext,
464                mSetupData.getAccount().mHostAuthRecv.mProtocol);
465        return useSsl ? info.portSsl : info.port;
466    }
467
468    private boolean getSslSelected() {
469        final int securityType =
470                (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
471        return ((securityType & HostAuth.FLAG_SSL) != 0);
472    }
473
474    public void onUseSslChanged(boolean useSsl) {
475        if (mServiceInfo.offerCerts) {
476            final int mode = useSsl ? View.VISIBLE : View.GONE;
477            mClientCertificateSelector.setVisibility(mode);
478            String deviceId = "";
479            try {
480                deviceId = Device.getDeviceId(mContext);
481            } catch (IOException e) {
482                // Not required
483            }
484            ((TextView) UiUtilities.getView(getView(), R.id.device_id)).setText(deviceId);
485
486            mDeviceIdSectionView.setVisibility(mode);
487            //UiUtilities.setVisibilitySafe(getView(), R.id.client_certificate_divider, mode);
488        }
489    }
490
491    private void updatePortFromSecurityType() {
492        final boolean sslSelected = getSslSelected();
493        final int port = getPortFromSecurityType(sslSelected);
494        mPortView.setText(Integer.toString(port));
495        onUseSslChanged(sslSelected);
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    @Override
505    public void saveSettingsAfterEdit() {
506        final Account account = mSetupData.getAccount();
507        account.update(mContext, account.toContentValues());
508        account.mHostAuthRecv.update(mContext, account.mHostAuthRecv.toContentValues());
509        // Update the backup (side copy) of the accounts
510        AccountBackupRestore.backup(mContext);
511    }
512
513    /**
514     * Entry point from Activity after entering new settings and verifying them.  For setup mode.
515     */
516    @Override
517    public void saveSettingsAfterSetup() {
518        final Account account = mSetupData.getAccount();
519        final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
520        final HostAuth sendAuth = account.getOrCreateHostAuthSend(mContext);
521
522        // Set the username and password for the outgoing settings to the username and
523        // password the user just set for incoming.  Use the verified host address to try and
524        // pick a smarter outgoing address.
525        final String hostName =
526                AccountSettingsUtils.inferServerName(mContext, recvAuth.mAddress, null, "smtp");
527        sendAuth.setLogin(recvAuth.mLogin, recvAuth.mPassword);
528        sendAuth.setConnection(sendAuth.mProtocol, hostName, sendAuth.mPort, sendAuth.mFlags);
529    }
530
531    /**
532     * Entry point from Activity, when "next" button is clicked
533     */
534    @Override
535    public void onNext() {
536        final Account account = mSetupData.getAccount();
537
538        // Make sure delete policy is an valid option before using it; otherwise, the results are
539        // indeterminate, I suspect...
540        if (mDeletePolicyView.getVisibility() == View.VISIBLE) {
541            account.setDeletePolicy(
542                    (Integer) ((SpinnerOption) mDeletePolicyView.getSelectedItem()).value);
543        }
544
545        final HostAuth recvAuth = account.getOrCreateHostAuthRecv(mContext);
546        final String userName = mUsernameView.getText().toString().trim();
547        final String userPassword = mPasswordView.getText().toString();
548        recvAuth.setLogin(userName, userPassword);
549
550        final String serverAddress = mServerView.getText().toString().trim();
551        int serverPort;
552        try {
553            serverPort = Integer.parseInt(mPortView.getText().toString().trim());
554        } catch (NumberFormatException e) {
555            serverPort = getPortFromSecurityType(getSslSelected());
556            LogUtils.d(Logging.LOG_TAG, "Non-integer server port; using '" + serverPort + "'");
557        }
558        final int securityType =
559                (Integer) ((SpinnerOption) mSecurityTypeView.getSelectedItem()).value;
560        recvAuth.setConnection(mBaseScheme, serverAddress, serverPort, securityType);
561        if (mServiceInfo.offerPrefix) {
562            final String prefix = mImapPathPrefixView.getText().toString().trim();
563            recvAuth.mDomain = TextUtils.isEmpty(prefix) ? null : ("/" + prefix);
564        } else {
565            recvAuth.mDomain = null;
566        }
567
568        // Check for a duplicate account (requires async DB work) and if OK,
569        // proceed with check
570        startDuplicateTaskCheck(
571                account.mId, serverAddress, mCacheLoginCredential, SetupData.CHECK_INCOMING);
572    }
573
574    @Override
575    public boolean haveSettingsChanged() {
576        final boolean deletePolicyChanged;
577
578        // Only verify the delete policy if the control is visible (i.e. is a pop3 account)
579        if (mDeletePolicyView != null && mDeletePolicyView.getVisibility() == View.VISIBLE) {
580            int newDeletePolicy =
581                (Integer)((SpinnerOption)mDeletePolicyView.getSelectedItem()).value;
582            deletePolicyChanged = mLoadedDeletePolicy != newDeletePolicy;
583        } else {
584            deletePolicyChanged = false;
585        }
586
587        return deletePolicyChanged || super.haveSettingsChanged();
588    }
589
590    /**
591     * Implements AccountCheckSettingsFragment.Callbacks
592     */
593    @Override
594    public void onAutoDiscoverComplete(int result, SetupData setupData) {
595        mSetupData = setupData;
596        final AccountSetupIncoming activity = (AccountSetupIncoming) getActivity();
597        activity.onAutoDiscoverComplete(result, setupData);
598    }
599
600    @Override
601    public void onCertificateRequested() {
602        final Intent intent = new Intent(CertificateRequestor.ACTION_REQUEST_CERT);
603        intent.setData(Uri.parse("eas://com.android.emailcommon/certrequest"));
604        startActivityForResult(intent, CERTIFICATE_REQUEST);
605    }
606
607    @Override
608    public void onActivityResult(int requestCode, int resultCode, Intent data) {
609        if (requestCode == CERTIFICATE_REQUEST && resultCode == Activity.RESULT_OK) {
610            final String certAlias = data.getStringExtra(CertificateRequestor.RESULT_ALIAS);
611            if (certAlias != null) {
612                mClientCertificateSelector.setCertificate(certAlias);
613            }
614        }
615    }
616}
617