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