AccountSetupOutgoing.java revision 17da1767e396b873723d53b2aef93da8aca2c00e
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 com.android.email.R;
20import com.android.email.Utility;
21import com.android.email.provider.EmailContent;
22
23import android.app.Activity;
24import android.content.Intent;
25import android.os.Bundle;
26import android.text.Editable;
27import android.text.TextWatcher;
28import android.text.method.DigitsKeyListener;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.View.OnClickListener;
32import android.widget.AdapterView;
33import android.widget.ArrayAdapter;
34import android.widget.Button;
35import android.widget.CheckBox;
36import android.widget.CompoundButton;
37import android.widget.EditText;
38import android.widget.Spinner;
39import android.widget.CompoundButton.OnCheckedChangeListener;
40
41import java.net.URI;
42import java.net.URISyntaxException;
43
44public class AccountSetupOutgoing extends Activity implements OnClickListener,
45        OnCheckedChangeListener {
46    private static final String EXTRA_ACCOUNT = "account";
47
48    private static final String EXTRA_MAKE_DEFAULT = "makeDefault";
49
50    private static final int smtpPorts[] = {
51            587, 465, 465, 587, 587
52    };
53
54    private static final String smtpSchemes[] = {
55            "smtp", "smtp+ssl+", "smtp+ssl+trustallcerts", "smtp+tls+", "smtp+tls+trustallcerts"
56    };
57
58    private EditText mUsernameView;
59    private EditText mPasswordView;
60    private EditText mServerView;
61    private EditText mPortView;
62    private CheckBox mRequireLoginView;
63    private ViewGroup mRequireLoginSettingsView;
64    private Spinner mSecurityTypeView;
65    private Button mNextButton;
66    private EmailContent.Account mAccount;
67    private boolean mMakeDefault;
68
69    public static void actionOutgoingSettings(Activity fromActivity, EmailContent.Account account,
70            boolean makeDefault) {
71        Intent i = new Intent(fromActivity, AccountSetupOutgoing.class);
72        i.putExtra(EXTRA_ACCOUNT, account);
73        i.putExtra(EXTRA_MAKE_DEFAULT, makeDefault);
74        fromActivity.startActivity(i);
75    }
76
77    public static void actionEditOutgoingSettings(Activity fromActivity, EmailContent.Account account)
78            {
79        Intent i = new Intent(fromActivity, AccountSetupOutgoing.class);
80        i.setAction(Intent.ACTION_EDIT);
81        i.putExtra(EXTRA_ACCOUNT, account);
82        fromActivity.startActivity(i);
83    }
84
85    @Override
86    public void onCreate(Bundle savedInstanceState) {
87        super.onCreate(savedInstanceState);
88        setContentView(R.layout.account_setup_outgoing);
89
90        mUsernameView = (EditText)findViewById(R.id.account_username);
91        mPasswordView = (EditText)findViewById(R.id.account_password);
92        mServerView = (EditText)findViewById(R.id.account_server);
93        mPortView = (EditText)findViewById(R.id.account_port);
94        mRequireLoginView = (CheckBox)findViewById(R.id.account_require_login);
95        mRequireLoginSettingsView = (ViewGroup)findViewById(R.id.account_require_login_settings);
96        mSecurityTypeView = (Spinner)findViewById(R.id.account_security_type);
97        mNextButton = (Button)findViewById(R.id.next);
98
99        mNextButton.setOnClickListener(this);
100        mRequireLoginView.setOnCheckedChangeListener(this);
101
102        SpinnerOption securityTypes[] = {
103            new SpinnerOption(0, getString(R.string.account_setup_incoming_security_none_label)),
104            new SpinnerOption(1, getString(R.string.account_setup_incoming_security_ssl_label)),
105            new SpinnerOption(2, getString(
106                    R.string.account_setup_incoming_security_ssl_trust_certificates_label)),
107            new SpinnerOption(3, getString(R.string.account_setup_incoming_security_tls_label)),
108            new SpinnerOption(4, getString(
109                    R.string.account_setup_incoming_security_tls_trust_certificates_label)),
110        };
111
112        ArrayAdapter<SpinnerOption> securityTypesAdapter = new ArrayAdapter<SpinnerOption>(this,
113                android.R.layout.simple_spinner_item, securityTypes);
114        securityTypesAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
115        mSecurityTypeView.setAdapter(securityTypesAdapter);
116
117        /*
118         * Updates the port when the user changes the security type. This allows
119         * us to show a reasonable default which the user can change.
120         */
121        mSecurityTypeView.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
122            public void onItemSelected(AdapterView arg0, View arg1, int arg2, long arg3) {
123                updatePortFromSecurityType();
124            }
125
126            public void onNothingSelected(AdapterView<?> arg0) {
127            }
128        });
129
130        /*
131         * Calls validateFields() which enables or disables the Next button
132         * based on the fields' validity.
133         */
134        TextWatcher validationTextWatcher = new TextWatcher() {
135            public void afterTextChanged(Editable s) {
136                validateFields();
137            }
138
139            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
140            }
141
142            public void onTextChanged(CharSequence s, int start, int before, int count) {
143            }
144        };
145        mUsernameView.addTextChangedListener(validationTextWatcher);
146        mPasswordView.addTextChangedListener(validationTextWatcher);
147        mServerView.addTextChangedListener(validationTextWatcher);
148        mPortView.addTextChangedListener(validationTextWatcher);
149
150        /*
151         * Only allow digits in the port field.
152         */
153        mPortView.setKeyListener(DigitsKeyListener.getInstance("0123456789"));
154
155        mAccount = (EmailContent.Account)getIntent().getParcelableExtra(EXTRA_ACCOUNT);
156        mMakeDefault = getIntent().getBooleanExtra(EXTRA_MAKE_DEFAULT, false);
157
158        /*
159         * If we're being reloaded we override the original account with the one
160         * we saved
161         */
162        if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) {
163            mAccount = (EmailContent.Account)savedInstanceState.getParcelable(EXTRA_ACCOUNT);
164        }
165
166        try {
167            // TODO this should be accessed directly via the HostAuth structure
168            URI uri = new URI(mAccount.getSenderUri(this));
169            String username = null;
170            String password = null;
171            if (uri.getUserInfo() != null) {
172                String[] userInfoParts = uri.getUserInfo().split(":", 2);
173                username = userInfoParts[0];
174                if (userInfoParts.length > 1) {
175                    password = userInfoParts[1];
176                }
177            }
178
179            if (username != null) {
180                mUsernameView.setText(username);
181                mRequireLoginView.setChecked(true);
182            }
183
184            if (password != null) {
185                mPasswordView.setText(password);
186            }
187
188            for (int i = 0; i < smtpSchemes.length; i++) {
189                if (smtpSchemes[i].equals(uri.getScheme())) {
190                    SpinnerOption.setSpinnerOptionValue(mSecurityTypeView, i);
191                }
192            }
193
194            if (uri.getHost() != null) {
195                mServerView.setText(uri.getHost());
196            }
197
198            if (uri.getPort() != -1) {
199                mPortView.setText(Integer.toString(uri.getPort()));
200            } else {
201                updatePortFromSecurityType();
202            }
203        } catch (URISyntaxException use) {
204            /*
205             * We should always be able to parse our own settings.
206             */
207            throw new Error(use);
208        }
209
210        validateFields();
211    }
212
213    @Override
214    public void onSaveInstanceState(Bundle outState) {
215        super.onSaveInstanceState(outState);
216        outState.putParcelable(EXTRA_ACCOUNT, mAccount);
217    }
218
219    /**
220     * Preflight the values in the fields and decide if it makes sense to enable the "next" button
221     * NOTE:  Does it make sense to extract & combine with similar code in AccountSetupIncoming?
222     */
223    private void validateFields() {
224        boolean enabled =
225            Utility.requiredFieldValid(mServerView) && Utility.requiredFieldValid(mPortView);
226
227        if (enabled && mRequireLoginView.isChecked()) {
228            enabled = (Utility.requiredFieldValid(mUsernameView)
229                    && Utility.requiredFieldValid(mPasswordView));
230        }
231
232        if (enabled) {
233            try {
234                URI uri = getUri();
235            } catch (URISyntaxException use) {
236                enabled = false;
237            }
238        }
239        mNextButton.setEnabled(enabled);
240        Utility.setCompoundDrawablesAlpha(mNextButton, enabled ? 255 : 128);
241    }
242
243    private void updatePortFromSecurityType() {
244        int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
245        mPortView.setText(Integer.toString(smtpPorts[securityType]));
246    }
247
248    @Override
249    public void onActivityResult(int requestCode, int resultCode, Intent data) {
250        if (resultCode == RESULT_OK) {
251            if (Intent.ACTION_EDIT.equals(getIntent().getAction())) {
252                if (mAccount.isSaved()) {
253                    mAccount.update(this, mAccount.toContentValues());
254                    mAccount.mHostAuthSend.update(this, mAccount.mHostAuthSend.toContentValues());
255               } else {
256                    mAccount.save(this);
257                }
258                finish();
259            } else {
260                AccountSetupOptions.actionOptions(this, mAccount, mMakeDefault, false);
261                finish();
262            }
263        }
264    }
265
266    /**
267     * Attempt to create a URI from the fields provided.  Throws URISyntaxException if there's
268     * a problem with the user input.
269     * @return a URI built from the account setup fields
270     */
271    private URI getUri() throws URISyntaxException {
272        int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value;
273        String userInfo = null;
274        if (mRequireLoginView.isChecked()) {
275            userInfo = mUsernameView.getText().toString().trim() + ":"
276                    + mPasswordView.getText().toString().trim();
277        }
278        URI uri = new URI(
279                smtpSchemes[securityType],
280                userInfo,
281                mServerView.getText().toString().trim(),
282                Integer.parseInt(mPortView.getText().toString().trim()),
283                null, null, null);
284
285        return uri;
286    }
287
288    private void onNext() {
289        try {
290            // TODO this should be accessed directly via the HostAuth structure
291            URI uri = getUri();
292            mAccount.setSenderUri(this, uri.toString());
293        } catch (URISyntaxException use) {
294            /*
295             * It's unrecoverable if we cannot create a URI from components that
296             * we validated to be safe.
297             */
298            throw new Error(use);
299        }
300        AccountSetupCheckSettings.actionValidateSettings(this, mAccount, false, true);
301    }
302
303    public void onClick(View v) {
304        switch (v.getId()) {
305            case R.id.next:
306                onNext();
307                break;
308        }
309    }
310
311    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
312        mRequireLoginSettingsView.setVisibility(isChecked ? View.VISIBLE : View.GONE);
313        validateFields();
314    }
315}
316