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