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