AccountSetupIncoming.java revision 96c5af40d639d629267794f4f0338a267ff94ce5
1/*
2 * Copyright (C) 2008 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 java.net.URI;
20import java.net.URISyntaxException;
21
22import android.app.Activity;
23import android.content.Intent;
24import android.os.Bundle;
25import android.text.Editable;
26import android.text.TextWatcher;
27import android.text.method.DigitsKeyListener;
28import android.view.View;
29import android.view.View.OnClickListener;
30import android.widget.AdapterView;
31import android.widget.ArrayAdapter;
32import android.widget.Button;
33import android.widget.EditText;
34import android.widget.Spinner;
35import android.widget.TextView;
36
37import com.android.email.Account;
38import com.android.email.Preferences;
39import com.android.email.R;
40import com.android.email.Utility;
41
42public class AccountSetupIncoming extends Activity implements OnClickListener {
43    private static final String EXTRA_ACCOUNT = "account";
44    private static final String EXTRA_MAKE_DEFAULT = "makeDefault";
45
46    private static final int popPorts[] = {
47            110, 995, 995, 110, 110
48    };
49    private static final String popSchemes[] = {
50            "pop3", "pop3+ssl", "pop3+ssl+", "pop3+tls", "pop3+tls+"
51    };
52    private static final int imapPorts[] = {
53            143, 993, 993, 143, 143
54    };
55    private static final String imapSchemes[] = {
56            "imap", "imap+ssl", "imap+ssl+", "imap+tls", "imap+tls+"
57    };
58
59    private int mAccountPorts[];
60    private String mAccountSchemes[];
61    private EditText mUsernameView;
62    private EditText mPasswordView;
63    private EditText mServerView;
64    private EditText mPortView;
65    private Spinner mSecurityTypeView;
66    private Spinner mDeletePolicyView;
67    private EditText mImapPathPrefixView;
68    private Button mNextButton;
69    private Account mAccount;
70    private boolean mMakeDefault;
71
72    public static void actionIncomingSettings(Activity context, Account account, boolean makeDefault) {
73        Intent i = new Intent(context, AccountSetupIncoming.class);
74        i.putExtra(EXTRA_ACCOUNT, account);
75        i.putExtra(EXTRA_MAKE_DEFAULT, makeDefault);
76        context.startActivity(i);
77    }
78
79    public static void actionEditIncomingSettings(Activity context, Account account) {
80        Intent i = new Intent(context, AccountSetupIncoming.class);
81        i.setAction(Intent.ACTION_EDIT);
82        i.putExtra(EXTRA_ACCOUNT, account);
83        context.startActivity(i);
84    }
85
86    @Override
87    public void onCreate(Bundle savedInstanceState) {
88        super.onCreate(savedInstanceState);
89        setContentView(R.layout.account_setup_incoming);
90
91        mUsernameView = (EditText)findViewById(R.id.account_username);
92        mPasswordView = (EditText)findViewById(R.id.account_password);
93        TextView serverLabelView = (TextView) findViewById(R.id.account_server_label);
94        mServerView = (EditText)findViewById(R.id.account_server);
95        mPortView = (EditText)findViewById(R.id.account_port);
96        mSecurityTypeView = (Spinner)findViewById(R.id.account_security_type);
97        mDeletePolicyView = (Spinner)findViewById(R.id.account_delete_policy);
98        mImapPathPrefixView = (EditText)findViewById(R.id.imap_path_prefix);
99        mNextButton = (Button)findViewById(R.id.next);
100
101        mNextButton.setOnClickListener(this);
102
103        SpinnerOption securityTypes[] = {
104                new SpinnerOption(0, getString(R.string.account_setup_incoming_security_none_label)),
105                new SpinnerOption(1,
106                        getString(R.string.account_setup_incoming_security_ssl_optional_label)),
107                new SpinnerOption(2, getString(R.string.account_setup_incoming_security_ssl_label)),
108                new SpinnerOption(3,
109                        getString(R.string.account_setup_incoming_security_tls_optional_label)),
110                new SpinnerOption(4, getString(R.string.account_setup_incoming_security_tls_label)),
111        };
112
113        SpinnerOption deletePolicies[] = {
114                new SpinnerOption(0,
115                        getString(R.string.account_setup_incoming_delete_policy_never_label)),
116                new SpinnerOption(1,
117                        getString(R.string.account_setup_incoming_delete_policy_7days_label)),
118                new SpinnerOption(2,
119                        getString(R.string.account_setup_incoming_delete_policy_delete_label)),
120        };
121
122        ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
123                android.R.layout.simple_spinner_item, securityTypes);
124        securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
125        mSecurityTypeView.setAdapter(securityTypesAdapter);
126
127        ArrayAdapter<SpinnerOption> deletePoliciesAdapter = new ArrayAdapter<SpinnerOption>(this,
128                android.R.layout.simple_spinner_item, deletePolicies);
129        deletePoliciesAdapter
130                .setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
131        mDeletePolicyView.setAdapter(deletePoliciesAdapter);
132
133        /*
134         * Updates the port when the user changes the security type. This allows
135         * us to show a reasonable default which the user can change.
136         */
137        mSecurityTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
138            public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) {
139                updatePortFromSecurityType();
140            }
141
142            public void onNothingSelected(AdapterView<?> arg0) {
143            }
144        });
145
146        /*
147         * Calls validateFields() which enables or disables the Next button
148         * based on the fields' validity.
149         */
150        TextWatcher validationTextWatcher = new TextWatcher() {
151            public void afterTextChanged(Editable s) {
152                validateFields();
153            }
154
155            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
156            }
157
158            public void onTextChanged(CharSequence s, int start, int before, int count) {
159            }
160        };
161        mUsernameView.addTextChangedListener(validationTextWatcher);
162        mPasswordView.addTextChangedListener(validationTextWatcher);
163        mServerView.addTextChangedListener(validationTextWatcher);
164        mPortView.addTextChangedListener(validationTextWatcher);
165
166        /*
167         * Only allow digits in the port field.
168         */
169        mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
170
171        mAccount = (Account)getIntent().getSerializableExtra(EXTRA_ACCOUNT);
172        mMakeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false);
173
174        /*
175         * If we're being reloaded we override the original account with the one
176         * we saved
177         */
178        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
179            mAccount = (Account)savedInstanceState.getSerializable(EXTRA_ACCOUNT);
180        }
181
182        try {
183            URI uri = new URI(mAccount.getStoreUri());
184            String username = null;
185            String password = null;
186            if (uri.getUserInfo() != null) {
187                String[] userInfoParts = uri.getUserInfo().split(":", 2);
188                username = userInfoParts[0];
189                if (userInfoParts.length > 1) {
190                    password = userInfoParts[1];
191                }
192            }
193
194            if (username != null) {
195                mUsernameView.setText(username);
196            }
197
198            if (password != null) {
199                mPasswordView.setText(password);
200            }
201
202            if (uri.getScheme().startsWith("pop3")) {
203                serverLabelView.setText(R.string.account_setup_incoming_pop_server_label);
204                mAccountPorts = popPorts;
205                mAccountSchemes = popSchemes;
206
207                findViewById(R.id.imap_path_prefix_section).setVisibility(View.GONE);
208            } else if (uri.getScheme().startsWith("imap")) {
209                serverLabelView.setText(R.string.account_setup_incoming_imap_server_label);
210                mAccountPorts = imapPorts;
211                mAccountSchemes = imapSchemes;
212
213                findViewById(R.id.account_delete_policy_label).setVisibility(View.GONE);
214                mDeletePolicyView.setVisibility(View.GONE);
215                if (uri.getPath() != null && uri.getPath().length() > 0) {
216                    mImapPathPrefixView.setText(uri.getPath().substring(1));
217                }
218            } else {
219                throw new Error("Unknown account type: " + mAccount.getStoreUri());
220            }
221
222            for (int i = 0; i < mAccountSchemes.length; i++) {
223                if (mAccountSchemes[i].equals(uri.getScheme())) {
224                    SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, i);
225                }
226            }
227
228            SpinnerOption.setSpinnerOptionValue(mDeletePolicyView, mAccount.getDeletePolicy());
229
230            if (uri.getHost() != null) {
231                mServerView.setText(uri.getHost());
232            }
233
234            if (uri.getPort() != -1) {
235                mPortView.setText(Integer.toString(uri.getPort()));
236            } else {
237                updatePortFromSecurityType();
238            }
239        } catch (URISyntaxException use) {
240            /*
241             * We should always be able to parse our own settings.
242             */
243            throw new Error(use);
244        }
245
246        validateFields();
247    }
248
249    @Override
250    public void onSaveInstanceState(Bundle outState) {
251        super.onSaveInstanceState(outState);
252        outState.putSerializable(EXTRA_ACCOUNT, mAccount);
253    }
254
255    /**
256     * Check the values in the fields and decide if it makes sense to enable the "next" button
257     * NOTE:  Does it make sense to extract & combine with similar code in AccountSetupIncoming?
258     */
259    private void validateFields() {
260        boolean enabled = Utility.requiredFieldValid(mUsernameView)
261                && Utility.requiredFieldValid(mPasswordView)
262                && Utility.requiredFieldValid(mServerView)
263                && Utility.requiredFieldValid(mPortView);
264        if (enabled) {
265            try {
266                URI uri = getUri();
267            } catch (URISyntaxException use) {
268                enabled = false;
269            }
270        }
271        mNextButton.setEnabled(enabled);
272        Utility.setCompoundDrawablesAlpha(mNextButton, enabled ? 255 : 128);
273    }
274
275    private void updatePortFromSecurityType() {
276        int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
277        mPortView.setText(Integer.toString(mAccountPorts[securityType]));
278    }
279
280    @Override
281    public void onActivityResult(int requestCode, int resultCode, Intent data) {
282        if (resultCode == RESULT_OK) {
283            if (Intent.ACTION_EDIT.equals(getIntent().getAction())) {
284                mAccount.save(Preferences.getPreferences(this));
285                finish();
286            } else {
287                /*
288                 * Set the username and password for the outgoing settings to the username and
289                 * password the user just set for incoming.
290                 */
291                try {
292                    URI oldUri = new URI(mAccount.getSenderUri());
293                    URI uri = new URI(
294                            oldUri.getScheme(),
295                            mUsernameView.getText().toString().trim() + ":"
296                                    + mPasswordView.getText().toString().trim(),
297                            oldUri.getHost(),
298                            oldUri.getPort(),
299                            null,
300                            null,
301                            null);
302                    mAccount.setSenderUri(uri.toString());
303                } catch (URISyntaxException use) {
304                    /*
305                     * If we can't set up the URL we just continue. It's only for
306                     * convenience.
307                     */
308                }
309
310
311                AccountSetupOutgoing.actionOutgoingSettings(this, mAccount, mMakeDefault);
312                finish();
313            }
314        }
315    }
316
317    /**
318     * Attempt to create a URI from the fields provided.  Throws URISyntaxException if there's
319     * a problem with the user input.
320     * @return a URI built from the account setup fields
321     */
322    private URI getUri() throws URISyntaxException {
323        int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
324        String path = null;
325        if (mAccountSchemes[securityType].startsWith("imap")) {
326            path = "/" + mImapPathPrefixView.getText().toString().trim();
327        }
328        URI uri = new URI(
329                mAccountSchemes[securityType],
330                mUsernameView.getText().toString().trim() + ":" +
331                        mPasswordView.getText().toString().trim(),
332                mServerView.getText().toString().trim(),
333                Integer.parseInt(mPortView.getText().toString().trim()),
334                path, // path
335                null, // query
336                null);
337
338        return uri;
339    }
340
341    private void onNext() {
342        try {
343            URI uri = getUri();
344            mAccount.setStoreUri(uri.toString());
345        } catch (URISyntaxException use) {
346            /*
347             * It's unrecoverable if we cannot create a URI from components that
348             * we validated to be safe.
349             */
350            throw new Error(use);
351        }
352
353        mAccount.setDeletePolicy((Integer)((SpinnerOption)mDeletePolicyView.getSelectedItem()).value);
354        AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, false);
355    }
356
357    public void onClick(View v) {
358        switch (v.getId()) {
359            case R.id.next:
360                onNext();
361                break;
362        }
363    }
364}
365