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.R; 20 import com.android.email.Utility; 21 import com.android.email.provider.EmailContent; 22 23 import android.app.Activity; 24 import android.content.Intent; 25 import android.os.Bundle; 26 import android.text.Editable; 27 import android.text.TextWatcher; 28 import android.text.method.DigitsKeyListener; 29 import android.view.View; 30 import android.view.ViewGroup; 31 import android.view.View.OnClickListener; 32 import android.widget.AdapterView; 33 import android.widget.ArrayAdapter; 34 import android.widget.Button; 35 import android.widget.CheckBox; 36 import android.widget.CompoundButton; 37 import android.widget.EditText; 38 import android.widget.Spinner; 39 import android.widget.CompoundButton.OnCheckedChangeListener; 40 41 import java.net.URI; 42 import java.net.URISyntaxException; 43 44 public 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 actionOutgoingSettings(Activity fromActivity, EmailContent.Account account, boolean makeDefault)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 actionEditOutgoingSettings(Activity fromActivity, EmailContent.Account account)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 onCreate(Bundle savedInstanceState)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 onSaveInstanceState(Bundle outState)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 */ validateFields()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 updatePortFromSecurityType()243 private void updatePortFromSecurityType() { 244 int securityType = (Integer)((SpinnerOption)mSecurityTypeView.getSelectedItem()).value; 245 mPortView.setText(Integer.toString(smtpPorts[securityType])); 246 } 247 248 @Override onActivityResult(int requestCode, int resultCode, Intent data)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 */ getUri()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 onNext()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.actionCheckSettings(this, mAccount, false, true); 301 } 302 onClick(View v)303 public void onClick(View v) { 304 switch (v.getId()) { 305 case R.id.next: 306 onNext(); 307 break; 308 } 309 } 310 onCheckedChanged(CompoundButton buttonView, boolean isChecked)311 public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 312 mRequireLoginSettingsView.setVisibility(isChecked ? View.VISIBLE : View.GONE); 313 validateFields(); 314 } 315 } 316