AccountSetupBasics.java revision be72b1d08af5851ba79651671df8c8b750e35f73
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.io.Serializable; 20import java.net.URI; 21import java.net.URISyntaxException; 22 23import android.app.Activity; 24import android.app.AlertDialog; 25import android.app.Dialog; 26import android.content.Context; 27import android.content.DialogInterface; 28import android.content.Intent; 29import android.content.res.XmlResourceParser; 30import android.database.Cursor; 31import android.net.Uri; 32import android.os.Bundle; 33import android.provider.Contacts; 34import android.provider.Contacts.People.ContactMethods; 35import android.text.Editable; 36import android.text.TextWatcher; 37import android.util.Log; 38import android.view.View; 39import android.view.View.OnClickListener; 40import android.widget.Button; 41import android.widget.CheckBox; 42import android.widget.EditText; 43import android.widget.Toast; 44 45import com.android.email.Account; 46import com.android.email.Email; 47import com.android.email.EmailAddressValidator; 48import com.android.email.Preferences; 49import com.android.email.R; 50import com.android.email.Utility; 51 52/** 53 * Prompts the user for the email address and password. Also prompts for 54 * "Use this account as default" if this is the 2nd+ account being set up. 55 * Attempts to lookup default settings for the domain the user specified. If the 56 * domain is known the settings are handed off to the AccountSetupCheckSettings 57 * activity. If no settings are found the settings are handed off to the 58 * AccountSetupAccountType activity. 59 */ 60public class AccountSetupBasics extends Activity 61 implements OnClickListener, TextWatcher { 62 private final static String EXTRA_ACCOUNT = "com.android.email.AccountSetupBasics.account"; 63 private final static int DIALOG_NOTE = 1; 64 private final static String STATE_KEY_PROVIDER = 65 "com.android.email.AccountSetupBasics.provider"; 66 67 // NOTE: If you change this value, confirm that the new interval exists in arrays.xml 68 private final static int DEFAULT_ACCOUNT_CHECK_INTERVAL = 15; 69 70 private Preferences mPrefs; 71 private EditText mEmailView; 72 private EditText mPasswordView; 73 private CheckBox mDefaultView; 74 private Button mNextButton; 75 private Button mManualSetupButton; 76 private Account mAccount; 77 private Provider mProvider; 78 79 private EmailAddressValidator mEmailValidator = new EmailAddressValidator(); 80 81 public static void actionNewAccount(Context context) { 82 Intent i = new Intent(context, AccountSetupBasics.class); 83 context.startActivity(i); 84 } 85 86 @Override 87 public void onCreate(Bundle savedInstanceState) { 88 super.onCreate(savedInstanceState); 89 setContentView(R.layout.account_setup_basics); 90 mPrefs = Preferences.getPreferences(this); 91 mEmailView = (EditText)findViewById(R.id.account_email); 92 mPasswordView = (EditText)findViewById(R.id.account_password); 93 mDefaultView = (CheckBox)findViewById(R.id.account_default); 94 mNextButton = (Button)findViewById(R.id.next); 95 mManualSetupButton = (Button)findViewById(R.id.manual_setup); 96 97 mNextButton.setOnClickListener(this); 98 mManualSetupButton.setOnClickListener(this); 99 100 mEmailView.addTextChangedListener(this); 101 mPasswordView.addTextChangedListener(this); 102 103 if (mPrefs.getAccounts().length > 0) { 104 mDefaultView.setVisibility(View.VISIBLE); 105 } 106 107 if (savedInstanceState != null && savedInstanceState.containsKey(EXTRA_ACCOUNT)) { 108 mAccount = (Account)savedInstanceState.getSerializable(EXTRA_ACCOUNT); 109 } 110 111 if (savedInstanceState != null && savedInstanceState.containsKey(STATE_KEY_PROVIDER)) { 112 mProvider = (Provider)savedInstanceState.getSerializable(STATE_KEY_PROVIDER); 113 } 114 } 115 116 @Override 117 public void onResume() { 118 super.onResume(); 119 validateFields(); 120 } 121 122 @Override 123 public void onSaveInstanceState(Bundle outState) { 124 super.onSaveInstanceState(outState); 125 outState.putSerializable(EXTRA_ACCOUNT, mAccount); 126 if (mProvider != null) { 127 outState.putSerializable(STATE_KEY_PROVIDER, mProvider); 128 } 129 } 130 131 public void afterTextChanged(Editable s) { 132 validateFields(); 133 } 134 135 public void beforeTextChanged(CharSequence s, int start, int count, int after) { 136 } 137 138 public void onTextChanged(CharSequence s, int start, int before, int count) { 139 } 140 141 private void validateFields() { 142 boolean valid = Utility.requiredFieldValid(mEmailView) 143 && Utility.requiredFieldValid(mPasswordView) 144 && mEmailValidator.isValid(mEmailView.getText().toString().trim()); 145 mNextButton.setEnabled(valid); 146 mManualSetupButton.setEnabled(valid); 147 /* 148 * Dim the next button's icon to 50% if the button is disabled. 149 * TODO this can probably be done with a stateful drawable. Check into it. 150 * android:state_enabled 151 */ 152 Utility.setCompoundDrawablesAlpha(mNextButton, mNextButton.isEnabled() ? 255 : 128); 153 } 154 155 private String getOwnerName() { 156 String name = null; 157 String projection[] = { 158 ContactMethods.NAME 159 }; 160 Cursor c = getContentResolver().query( 161 Uri.withAppendedPath(Contacts.People.CONTENT_URI, "owner"), projection, null, null, 162 null); 163 if (c.getCount() > 0) { 164 c.moveToFirst(); 165 name = c.getString(0); 166 c.close(); 167 } 168 169 if (name == null || name.length() == 0) { 170 Account account = Preferences.getPreferences(this).getDefaultAccount(); 171 if (account != null) { 172 name = account.getName(); 173 } 174 } 175 return name; 176 } 177 178 @Override 179 public Dialog onCreateDialog(int id) { 180 if (id == DIALOG_NOTE) { 181 if (mProvider != null && mProvider.note != null) { 182 return new AlertDialog.Builder(this) 183 .setMessage(mProvider.note) 184 .setPositiveButton( 185 getString(R.string.okay_action), 186 new DialogInterface.OnClickListener() { 187 public void onClick(DialogInterface dialog, int which) { 188 finishAutoSetup(); 189 } 190 }) 191 .setNegativeButton( 192 getString(R.string.cancel_action), 193 null) 194 .create(); 195 } 196 } 197 return null; 198 } 199 200 private void finishAutoSetup() { 201 String email = mEmailView.getText().toString().trim(); 202 String password = mPasswordView.getText().toString().trim(); 203 String[] emailParts = email.split("@"); 204 String user = emailParts[0]; 205 String domain = emailParts[1]; 206 URI incomingUri = null; 207 URI outgoingUri = null; 208 try { 209 String incomingUsername = mProvider.incomingUsernameTemplate; 210 incomingUsername = incomingUsername.replaceAll("\\$email", email); 211 incomingUsername = incomingUsername.replaceAll("\\$user", user); 212 incomingUsername = incomingUsername.replaceAll("\\$domain", domain); 213 214 URI incomingUriTemplate = mProvider.incomingUriTemplate; 215 incomingUri = new URI(incomingUriTemplate.getScheme(), incomingUsername + ":" 216 + password, incomingUriTemplate.getHost(), incomingUriTemplate.getPort(), null, 217 null, null); 218 219 String outgoingUsername = mProvider.outgoingUsernameTemplate; 220 outgoingUsername = outgoingUsername.replaceAll("\\$email", email); 221 outgoingUsername = outgoingUsername.replaceAll("\\$user", user); 222 outgoingUsername = outgoingUsername.replaceAll("\\$domain", domain); 223 224 URI outgoingUriTemplate = mProvider.outgoingUriTemplate; 225 outgoingUri = new URI(outgoingUriTemplate.getScheme(), outgoingUsername + ":" 226 + password, outgoingUriTemplate.getHost(), outgoingUriTemplate.getPort(), null, 227 null, null); 228 } catch (URISyntaxException use) { 229 /* 230 * If there is some problem with the URI we give up and go on to 231 * manual setup. 232 */ 233 onManualSetup(); 234 return; 235 } 236 237 mAccount = new Account(this); 238 mAccount.setName(getOwnerName()); 239 mAccount.setEmail(email); 240 mAccount.setStoreUri(incomingUri.toString()); 241 mAccount.setSenderUri(outgoingUri.toString()); 242 mAccount.setDraftsFolderName(getString(R.string.special_mailbox_name_drafts)); 243 mAccount.setTrashFolderName(getString(R.string.special_mailbox_name_trash)); 244 mAccount.setOutboxFolderName(getString(R.string.special_mailbox_name_outbox)); 245 mAccount.setSentFolderName(getString(R.string.special_mailbox_name_sent)); 246 if (incomingUri.toString().startsWith("imap")) { 247 // Delete policy must be set explicitly, because IMAP does not provide a UI selection 248 // for it. This logic needs to be followed in the auto setup flow as well. 249 mAccount.setDeletePolicy(Account.DELETE_POLICY_ON_DELETE); 250 } 251 mAccount.setAutomaticCheckIntervalMinutes(DEFAULT_ACCOUNT_CHECK_INTERVAL); 252 AccountSetupCheckSettings.actionCheckSettings(this, mAccount, true, true); 253 } 254 255 private void onNext() { 256 String email = mEmailView.getText().toString().trim(); 257 String[] emailParts = email.split("@"); 258 String domain = emailParts[1].trim(); 259 mProvider = findProviderForDomain(domain); 260 if (mProvider == null) { 261 /* 262 * We don't have default settings for this account, start the manual 263 * setup process. 264 */ 265 onManualSetup(); 266 return; 267 } 268 269 if (mProvider.note != null) { 270 showDialog(DIALOG_NOTE); 271 } 272 else { 273 finishAutoSetup(); 274 } 275 } 276 277 @Override 278 public void onActivityResult(int requestCode, int resultCode, Intent data) { 279 if (resultCode == RESULT_OK) { 280 mAccount.setDescription(mAccount.getEmail()); 281 mAccount.save(Preferences.getPreferences(this)); 282 if (mDefaultView.isChecked()) { 283 Preferences.getPreferences(this).setDefaultAccount(mAccount); 284 } 285 Email.setServicesEnabled(this); 286 AccountSetupNames.actionSetNames(this, mAccount); 287 finish(); 288 } 289 } 290 291 private void onManualSetup() { 292 String email = mEmailView.getText().toString().trim(); 293 String password = mPasswordView.getText().toString().trim(); 294 String[] emailParts = email.split("@"); 295 String user = emailParts[0].trim(); 296 String domain = emailParts[1].trim(); 297 298 mAccount = new Account(this); 299 mAccount.setName(getOwnerName()); 300 mAccount.setEmail(email); 301 try { 302 URI uri = new URI("placeholder", user + ":" + password, domain, -1, null, null, null); 303 mAccount.setStoreUri(uri.toString()); 304 mAccount.setSenderUri(uri.toString()); 305 } catch (URISyntaxException use) { 306 // If we can't set up the URL, don't continue - account setup pages will fail too 307 Toast.makeText(this, R.string.account_setup_username_password_toast, Toast.LENGTH_LONG) 308 .show(); 309 mAccount = null; 310 return; 311 } 312 mAccount.setDraftsFolderName(getString(R.string.special_mailbox_name_drafts)); 313 mAccount.setTrashFolderName(getString(R.string.special_mailbox_name_trash)); 314 mAccount.setOutboxFolderName(getString(R.string.special_mailbox_name_outbox)); 315 mAccount.setSentFolderName(getString(R.string.special_mailbox_name_sent)); 316 317 mAccount.setAutomaticCheckIntervalMinutes(DEFAULT_ACCOUNT_CHECK_INTERVAL); 318 319 AccountSetupAccountType.actionSelectAccountType(this, mAccount, mDefaultView.isChecked()); 320 finish(); 321 } 322 323 public void onClick(View v) { 324 switch (v.getId()) { 325 case R.id.next: 326 onNext(); 327 break; 328 case R.id.manual_setup: 329 onManualSetup(); 330 break; 331 } 332 } 333 334 /** 335 * Attempts to get the given attribute as a String resource first, and if it fails 336 * returns the attribute as a simple String value. 337 * @param xml 338 * @param name 339 * @return 340 */ 341 private String getXmlAttribute(XmlResourceParser xml, String name) { 342 int resId = xml.getAttributeResourceValue(null, name, 0); 343 if (resId == 0) { 344 return xml.getAttributeValue(null, name); 345 } 346 else { 347 return getString(resId); 348 } 349 } 350 351 /** 352 * Search the list of known Email providers looking for one that matches the user's email 353 * domain. We look in providers_product.xml first, followed by the entries in 354 * platform providers.xml. This provides a nominal override capability. 355 * 356 * A match is defined as any provider entry for which the "domain" attribute matches. 357 * 358 * @param domain The domain portion of the user's email address 359 * @return suitable Provider definition, or null if no match found 360 */ 361 private Provider findProviderForDomain(String domain) { 362 Provider p = findProviderForDomain(domain, R.xml.providers_product); 363 if (p == null) { 364 p = findProviderForDomain(domain, R.xml.providers); 365 } 366 return p; 367 } 368 369 /** 370 * Search a single resource containing known Email provider definitions. 371 * 372 * @param domain The domain portion of the user's email address 373 * @param resourceId Id of the provider resource to scan 374 * @return suitable Provider definition, or null if no match found 375 */ 376 private Provider findProviderForDomain(String domain, int resourceId) { 377 try { 378 XmlResourceParser xml = getResources().getXml(resourceId); 379 int xmlEventType; 380 Provider provider = null; 381 while ((xmlEventType = xml.next()) != XmlResourceParser.END_DOCUMENT) { 382 if (xmlEventType == XmlResourceParser.START_TAG 383 && "provider".equals(xml.getName()) 384 && domain.equalsIgnoreCase(getXmlAttribute(xml, "domain"))) { 385 provider = new Provider(); 386 provider.id = getXmlAttribute(xml, "id"); 387 provider.label = getXmlAttribute(xml, "label"); 388 provider.domain = getXmlAttribute(xml, "domain"); 389 provider.note = getXmlAttribute(xml, "note"); 390 } 391 else if (xmlEventType == XmlResourceParser.START_TAG 392 && "incoming".equals(xml.getName()) 393 && provider != null) { 394 provider.incomingUriTemplate = new URI(getXmlAttribute(xml, "uri")); 395 provider.incomingUsernameTemplate = getXmlAttribute(xml, "username"); 396 } 397 else if (xmlEventType == XmlResourceParser.START_TAG 398 && "outgoing".equals(xml.getName()) 399 && provider != null) { 400 provider.outgoingUriTemplate = new URI(getXmlAttribute(xml, "uri")); 401 provider.outgoingUsernameTemplate = getXmlAttribute(xml, "username"); 402 } 403 else if (xmlEventType == XmlResourceParser.END_TAG 404 && "provider".equals(xml.getName()) 405 && provider != null) { 406 return provider; 407 } 408 } 409 } 410 catch (Exception e) { 411 Log.e(Email.LOG_TAG, "Error while trying to load provider settings.", e); 412 } 413 return null; 414 } 415 416 static class Provider implements Serializable { 417 private static final long serialVersionUID = 8511656164616538989L; 418 419 public String id; 420 421 public String label; 422 423 public String domain; 424 425 public URI incomingUriTemplate; 426 427 public String incomingUsernameTemplate; 428 429 public URI outgoingUriTemplate; 430 431 public String outgoingUsernameTemplate; 432 433 public String note; 434 } 435} 436