Account.java revision afe5e27521b98540a17ec352d16e3641fea8dfd6
1/** 2 * Copyright (c) 2012, Google Inc. 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.mail.providers; 18 19import android.content.ContentResolver; 20import android.content.ContentValues; 21import android.database.Cursor; 22import android.database.MatrixCursor; 23import android.net.Uri; 24import android.os.Parcel; 25import android.os.Parcelable; 26import android.text.TextUtils; 27 28import com.android.mail.content.CursorCreator; 29import com.android.mail.content.ObjectCursor; 30import com.android.mail.providers.UIProvider.AccountCapabilities; 31import com.android.mail.providers.UIProvider.AccountColumns; 32import com.android.mail.providers.UIProvider.SyncStatus; 33import com.android.mail.utils.LogTag; 34import com.android.mail.utils.LogUtils; 35import com.android.mail.utils.Utils; 36import com.google.common.base.Objects; 37import com.google.common.collect.Lists; 38 39import org.json.JSONArray; 40import org.json.JSONException; 41import org.json.JSONObject; 42 43import java.util.HashMap; 44import java.util.List; 45import java.util.Map; 46 47public class Account implements Parcelable { 48 private static final String SETTINGS_KEY = "settings"; 49 50 /** 51 * Human readable account name. Not guaranteed to be the account's email address, nor to match 52 * the system account manager. 53 */ 54 // TODO: Make this private and add getDisplayName() accessor 55 public final String name; 56 57 /** 58 * The real name associated with the account, e.g. "John Doe" 59 */ 60 private final String senderName; 61 62 /** 63 * Account manager name. MUST MATCH SYSTEM ACCOUNT MANAGER NAME 64 */ 65 66 private final String accountManagerName; 67 68 /** 69 * Account type. MUST MATCH SYSTEM ACCOUNT MANAGER TYPE 70 */ 71 72 private final String type; 73 74 /** 75 * Cached android.accounts.Account based on the above two values 76 */ 77 78 private android.accounts.Account amAccount; 79 80 /** 81 * The version of the UI provider schema from which this account provider 82 * will return results. 83 */ 84 public final int providerVersion; 85 86 /** 87 * The uri to directly access the information for this account. 88 */ 89 public final Uri uri; 90 91 /** 92 * The possible capabilities that this account supports. 93 */ 94 public final int capabilities; 95 96 /** 97 * The content provider uri to return the list of top level folders for this 98 * account. 99 */ 100 public final Uri folderListUri; 101 /** 102 * The content provider uri to return the list of all real folders for this 103 * account. 104 */ 105 public Uri fullFolderListUri; 106 /** 107 * The content provider uri to return the list of all real and synthetic folders for this 108 * account. 109 */ 110 public Uri allFolderListUri; 111 /** 112 * The content provider uri that can be queried for search results. 113 */ 114 public final Uri searchUri; 115 116 /** 117 * The custom from addresses for this account or null if there are none. 118 */ 119 public String accountFromAddresses; 120 121 /** 122 * The content provider uri that can be used to expunge message from this 123 * account. NOTE: This might be better to be an update operation on the 124 * messageUri. 125 */ 126 public final Uri expungeMessageUri; 127 128 /** 129 * The content provider uri that can be used to undo the last operation 130 * performed. 131 */ 132 public final Uri undoUri; 133 134 /** 135 * Uri for EDIT intent that will cause the settings screens for this account type to be 136 * shown. 137 */ 138 public final Uri settingsIntentUri; 139 140 /** 141 * Uri for VIEW intent that will cause the help screens for this account type to be 142 * shown. 143 */ 144 public final Uri helpIntentUri; 145 146 /** 147 * Uri for VIEW intent that will cause the send feedback screens for this account type to be 148 * shown. 149 */ 150 public final Uri sendFeedbackIntentUri; 151 152 /** 153 * Uri for VIEW intent that will cause the reauthentication screen for this account to be 154 * shown. 155 */ 156 public final Uri reauthenticationIntentUri; 157 158 /** 159 * The sync status of the account 160 */ 161 public final int syncStatus; 162 163 /** 164 * Uri for VIEW intent that will cause the compose screen for this account type to be 165 * shown. 166 */ 167 public final Uri composeIntentUri; 168 169 public final String mimeType; 170 /** 171 * URI for recent folders for this account. 172 */ 173 public final Uri recentFolderListUri; 174 /** 175 * The color used for this account in combined view (Email) 176 */ 177 public final int color; 178 /** 179 * URI for default recent folders for this account, if any. 180 */ 181 public final Uri defaultRecentFolderListUri; 182 /** 183 * Settings object for this account. 184 */ 185 public final Settings settings; 186 187 /** 188 * URI for forcing a manual sync of this account. 189 */ 190 public final Uri manualSyncUri; 191 192 /** 193 * URI for account type specific supplementary account info on outgoing links, if any. 194 */ 195 public final Uri viewIntentProxyUri; 196 197 /** 198 * URI for querying for the account cookies to be used when displaying inline content in a 199 * conversation 200 */ 201 public final Uri accoutCookieQueryUri; 202 203 /** 204 * URI to be used with an update() ContentResolver call with a {@link ContentValues} object 205 * where the keys are from the {@link AccountColumns.SettingsColumns}, and the values are the 206 * new values. 207 */ 208 public final Uri updateSettingsUri; 209 210 /** 211 * Whether message transforms (HTML DOM manipulation) feature is enabled. 212 */ 213 public final int enableMessageTransforms; 214 215 /** 216 * Sync authority used by the mail app. This can be used in 217 * {@link ContentResolver#getSyncAutomatically} calls to check for whether sync is enabled 218 * for this account and mail app. 219 */ 220 public final String syncAuthority; 221 222 public final Uri quickResponseUri; 223 224 /** 225 * Transient cache of parsed {@link #accountFromAddresses}, plus an entry for the main account 226 * address. 227 */ 228 private transient List<ReplyFromAccount> mReplyFroms; 229 230 private static final String LOG_TAG = LogTag.getLogTag(); 231 232 /** 233 * Return a serialized String for this account. 234 */ 235 public synchronized String serialize() { 236 JSONObject json = new JSONObject(); 237 try { 238 json.put(AccountColumns.NAME, name); 239 json.put(AccountColumns.TYPE, type); 240 json.put(AccountColumns.SENDER_NAME, senderName); 241 json.put(AccountColumns.ACCOUNT_MANAGER_NAME, accountManagerName); 242 json.put(AccountColumns.PROVIDER_VERSION, providerVersion); 243 json.put(AccountColumns.URI, uri); 244 json.put(AccountColumns.CAPABILITIES, capabilities); 245 json.put(AccountColumns.FOLDER_LIST_URI, folderListUri); 246 json.put(AccountColumns.FULL_FOLDER_LIST_URI, fullFolderListUri); 247 json.put(AccountColumns.ALL_FOLDER_LIST_URI, allFolderListUri); 248 json.put(AccountColumns.SEARCH_URI, searchUri); 249 json.put(AccountColumns.ACCOUNT_FROM_ADDRESSES, accountFromAddresses); 250 json.put(AccountColumns.EXPUNGE_MESSAGE_URI, expungeMessageUri); 251 json.put(AccountColumns.UNDO_URI, undoUri); 252 json.put(AccountColumns.SETTINGS_INTENT_URI, settingsIntentUri); 253 json.put(AccountColumns.HELP_INTENT_URI, helpIntentUri); 254 json.put(AccountColumns.SEND_FEEDBACK_INTENT_URI, sendFeedbackIntentUri); 255 json.put(AccountColumns.REAUTHENTICATION_INTENT_URI, reauthenticationIntentUri); 256 json.put(AccountColumns.SYNC_STATUS, syncStatus); 257 json.put(AccountColumns.COMPOSE_URI, composeIntentUri); 258 json.put(AccountColumns.MIME_TYPE, mimeType); 259 json.put(AccountColumns.RECENT_FOLDER_LIST_URI, recentFolderListUri); 260 json.put(AccountColumns.COLOR, color); 261 json.put(AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI, defaultRecentFolderListUri); 262 json.put(AccountColumns.MANUAL_SYNC_URI, manualSyncUri); 263 json.put(AccountColumns.VIEW_INTENT_PROXY_URI, viewIntentProxyUri); 264 json.put(AccountColumns.ACCOUNT_COOKIE_QUERY_URI, accoutCookieQueryUri); 265 json.put(AccountColumns.UPDATE_SETTINGS_URI, updateSettingsUri); 266 json.put(AccountColumns.ENABLE_MESSAGE_TRANSFORMS, enableMessageTransforms); 267 json.put(AccountColumns.SYNC_AUTHORITY, syncAuthority); 268 json.put(AccountColumns.QUICK_RESPONSE_URI, quickResponseUri); 269 if (settings != null) { 270 json.put(SETTINGS_KEY, settings.toJSON()); 271 } 272 } catch (JSONException e) { 273 LogUtils.wtf(LOG_TAG, e, "Could not serialize account with name %s", name); 274 } 275 return json.toString(); 276 } 277 278 /** 279 * Create a new instance of an Account object using a serialized instance created previously 280 * using {@link #serialize()}. This returns null if the serialized instance was invalid or does 281 * not represent a valid account object. 282 * 283 * @param serializedAccount JSON encoded account object 284 * @return Account object 285 */ 286 public static Account newinstance(String serializedAccount) { 287 // The heavy lifting is done by Account(name, type, json). This method 288 // is a wrapper to check for errors and exceptions and return back a null in cases 289 // something breaks. 290 try { 291 final JSONObject json = new JSONObject(serializedAccount); 292 final String name = (String) json.get(UIProvider.AccountColumns.NAME); 293 final String type = (String) json.get(UIProvider.AccountColumns.TYPE); 294 return new Account(name, type, json); 295 } catch (JSONException e) { 296 LogUtils.w(LOG_TAG, e, "Could not create an account from this input: \"%s\"", 297 serializedAccount); 298 return null; 299 } 300 } 301 302 /** 303 * Construct a new Account instance from a previously serialized string. This calls 304 * {@link android.accounts.Account#Account(String, String)} with name and type given as the 305 * first two arguments. 306 * 307 * <p> 308 * This is private. Public uses should go through the safe {@link #newinstance(String)} method. 309 * </p> 310 * @param acctName name of account in {@link android.accounts.Account} 311 * @param acctType type of account in {@link android.accounts.Account} 312 * @param {@link JSONObject} representing a valid account. 313 * @throws JSONException 314 */ 315 private Account(String acctName, String acctType, JSONObject json) throws JSONException { 316 name = acctName; 317 type = acctType; 318 senderName = json.optString(AccountColumns.SENDER_NAME, null); 319 final String amName = json.optString(AccountColumns.ACCOUNT_MANAGER_NAME); 320 // We need accountManagerName to be filled in, but we might be dealing with an old cache 321 // entry which doesn't have it, so use the display name instead in that case as a fallback 322 if (TextUtils.isEmpty(amName)) { 323 accountManagerName = name; 324 } else { 325 accountManagerName = amName; 326 } 327 providerVersion = json.getInt(AccountColumns.PROVIDER_VERSION); 328 uri = Uri.parse(json.optString(AccountColumns.URI)); 329 capabilities = json.getInt(AccountColumns.CAPABILITIES); 330 folderListUri = Utils 331 .getValidUri(json.optString(AccountColumns.FOLDER_LIST_URI)); 332 fullFolderListUri = Utils.getValidUri(json 333 .optString(AccountColumns.FULL_FOLDER_LIST_URI)); 334 allFolderListUri = Utils.getValidUri(json 335 .optString(AccountColumns.ALL_FOLDER_LIST_URI)); 336 searchUri = Utils.getValidUri(json.optString(AccountColumns.SEARCH_URI)); 337 accountFromAddresses = json.optString(AccountColumns.ACCOUNT_FROM_ADDRESSES, 338 ""); 339 expungeMessageUri = Utils.getValidUri(json 340 .optString(AccountColumns.EXPUNGE_MESSAGE_URI)); 341 undoUri = Utils.getValidUri(json.optString(AccountColumns.UNDO_URI)); 342 settingsIntentUri = Utils.getValidUri(json 343 .optString(AccountColumns.SETTINGS_INTENT_URI)); 344 helpIntentUri = Utils.getValidUri(json.optString(AccountColumns.HELP_INTENT_URI)); 345 sendFeedbackIntentUri = Utils.getValidUri(json 346 .optString(AccountColumns.SEND_FEEDBACK_INTENT_URI)); 347 reauthenticationIntentUri = Utils.getValidUri( 348 json.optString(AccountColumns.REAUTHENTICATION_INTENT_URI)); 349 syncStatus = json.optInt(AccountColumns.SYNC_STATUS); 350 composeIntentUri = Utils.getValidUri(json.optString(AccountColumns.COMPOSE_URI)); 351 mimeType = json.optString(AccountColumns.MIME_TYPE); 352 recentFolderListUri = Utils.getValidUri(json 353 .optString(AccountColumns.RECENT_FOLDER_LIST_URI)); 354 color = json.optInt(AccountColumns.COLOR, 0); 355 defaultRecentFolderListUri = Utils.getValidUri(json 356 .optString(AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI)); 357 manualSyncUri = Utils 358 .getValidUri(json.optString(AccountColumns.MANUAL_SYNC_URI)); 359 viewIntentProxyUri = Utils 360 .getValidUri(json.optString(AccountColumns.VIEW_INTENT_PROXY_URI)); 361 accoutCookieQueryUri = Utils.getValidUri( 362 json.optString(AccountColumns.ACCOUNT_COOKIE_QUERY_URI)); 363 updateSettingsUri = Utils.getValidUri( 364 json.optString(AccountColumns.UPDATE_SETTINGS_URI)); 365 enableMessageTransforms = json.optInt(AccountColumns.ENABLE_MESSAGE_TRANSFORMS); 366 syncAuthority = json.optString(AccountColumns.SYNC_AUTHORITY); 367 quickResponseUri = Utils.getValidUri(json.optString(AccountColumns.QUICK_RESPONSE_URI)); 368 369 final Settings jsonSettings = Settings.newInstance(json.optJSONObject(SETTINGS_KEY)); 370 if (jsonSettings != null) { 371 settings = jsonSettings; 372 } else { 373 LogUtils.e(LOG_TAG, new Throwable(), 374 "Unexpected null settings in Account(name, type, jsonAccount)"); 375 settings = Settings.EMPTY_SETTINGS; 376 } 377 } 378 379 public Account(Cursor cursor) { 380 name = cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.NAME)); 381 senderName = cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.SENDER_NAME)); 382 type = cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.TYPE)); 383 accountManagerName = cursor.getString( 384 cursor.getColumnIndex(UIProvider.AccountColumns.ACCOUNT_MANAGER_NAME)); 385 accountFromAddresses = cursor.getString( 386 cursor.getColumnIndex(UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES)); 387 388 final int capabilitiesColumnIndex = 389 cursor.getColumnIndex(UIProvider.AccountColumns.CAPABILITIES); 390 if (capabilitiesColumnIndex != -1) { 391 capabilities = cursor.getInt(capabilitiesColumnIndex); 392 } else { 393 capabilities = 0; 394 } 395 396 providerVersion = 397 cursor.getInt(cursor.getColumnIndex(UIProvider.AccountColumns.PROVIDER_VERSION)); 398 uri = Uri.parse(cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.URI))); 399 folderListUri = Uri.parse( 400 cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.FOLDER_LIST_URI))); 401 fullFolderListUri = Utils.getValidUri(cursor.getString( 402 cursor.getColumnIndex(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI))); 403 allFolderListUri = Utils.getValidUri(cursor.getString( 404 cursor.getColumnIndex(UIProvider.AccountColumns.ALL_FOLDER_LIST_URI))); 405 searchUri = Utils.getValidUri( 406 cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.SEARCH_URI))); 407 expungeMessageUri = Utils.getValidUri(cursor.getString( 408 cursor.getColumnIndex(UIProvider.AccountColumns.EXPUNGE_MESSAGE_URI))); 409 undoUri = Utils.getValidUri( 410 cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.UNDO_URI))); 411 settingsIntentUri = Utils.getValidUri(cursor.getString( 412 cursor.getColumnIndex(UIProvider.AccountColumns.SETTINGS_INTENT_URI))); 413 helpIntentUri = Utils.getValidUri( 414 cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.HELP_INTENT_URI))); 415 sendFeedbackIntentUri = Utils.getValidUri(cursor.getString( 416 cursor.getColumnIndex(UIProvider.AccountColumns.SEND_FEEDBACK_INTENT_URI))); 417 reauthenticationIntentUri = Utils.getValidUri(cursor.getString( 418 cursor.getColumnIndex(UIProvider.AccountColumns.REAUTHENTICATION_INTENT_URI))); 419 syncStatus = cursor.getInt(cursor.getColumnIndex(UIProvider.AccountColumns.SYNC_STATUS)); 420 composeIntentUri = Utils.getValidUri( 421 cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.COMPOSE_URI))); 422 mimeType = cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.MIME_TYPE)); 423 recentFolderListUri = Utils.getValidUri(cursor.getString( 424 cursor.getColumnIndex(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI))); 425 color = cursor.getInt(cursor.getColumnIndex(UIProvider.AccountColumns.COLOR)); 426 defaultRecentFolderListUri = Utils.getValidUri(cursor.getString( 427 cursor.getColumnIndex(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI))); 428 manualSyncUri = Utils.getValidUri( 429 cursor.getString(cursor.getColumnIndex(UIProvider.AccountColumns.MANUAL_SYNC_URI))); 430 viewIntentProxyUri = Utils.getValidUri(cursor.getString( 431 cursor.getColumnIndex(UIProvider.AccountColumns.VIEW_INTENT_PROXY_URI))); 432 accoutCookieQueryUri = Utils.getValidUri(cursor.getString( 433 cursor.getColumnIndex(UIProvider.AccountColumns.ACCOUNT_COOKIE_QUERY_URI))); 434 updateSettingsUri = Utils.getValidUri(cursor.getString( 435 cursor.getColumnIndex(UIProvider.AccountColumns.UPDATE_SETTINGS_URI))); 436 enableMessageTransforms = cursor.getInt( 437 cursor.getColumnIndex(AccountColumns.ENABLE_MESSAGE_TRANSFORMS)); 438 syncAuthority = cursor.getString( 439 cursor.getColumnIndex(AccountColumns.SYNC_AUTHORITY)); 440 if (TextUtils.isEmpty(syncAuthority)) { 441 // NOTE: this is actually expected in Email for the "combined view" account only 442 LogUtils.e(LOG_TAG, "Unexpected empty syncAuthority from cursor"); 443 } 444 quickResponseUri = Utils.getValidUri(cursor.getString( 445 cursor.getColumnIndex(AccountColumns.QUICK_RESPONSE_URI))); 446 settings = new Settings(cursor); 447 } 448 449 /** 450 * Returns an array of all Accounts located at this cursor. This method returns a zero length 451 * array if no account was found. This method does not close the cursor. 452 * @param cursor cursor pointing to the list of accounts 453 * @return the array of all accounts stored at this cursor. 454 */ 455 public static Account[] getAllAccounts(ObjectCursor<Account> cursor) { 456 final int initialLength = cursor.getCount(); 457 if (initialLength <= 0 || !cursor.moveToFirst()) { 458 // Return zero length account array rather than null 459 return new Account[0]; 460 } 461 462 final Account[] allAccounts = new Account[initialLength]; 463 int i = 0; 464 do { 465 allAccounts[i++] = cursor.getModel(); 466 } while (cursor.moveToNext()); 467 // Ensure that the length of the array is accurate 468 assert (i == initialLength); 469 return allAccounts; 470 } 471 472 public android.accounts.Account getAccountManagerAccount() { 473 if (amAccount == null) { 474 // We don't really need to synchronize this 475 // as worst case is we'd create an extra identical object and throw it away 476 amAccount = new android.accounts.Account(accountManagerName, type); 477 } 478 return amAccount; 479 } 480 481 public boolean supportsCapability(int capability) { 482 return (capabilities & capability) != 0; 483 } 484 485 public boolean isAccountSyncRequired() { 486 return (syncStatus & SyncStatus.INITIAL_SYNC_NEEDED) == SyncStatus.INITIAL_SYNC_NEEDED; 487 } 488 489 public boolean isAccountInitializationRequired() { 490 return (syncStatus & SyncStatus.ACCOUNT_INITIALIZATION_REQUIRED) == 491 SyncStatus.ACCOUNT_INITIALIZATION_REQUIRED; 492 } 493 494 /** 495 * Returns true when when the UI provider has indicated that the account has been initialized, 496 * and sync is not required. 497 */ 498 public boolean isAccountReady() { 499 return !isAccountInitializationRequired() && !isAccountSyncRequired(); 500 } 501 502 public Account(Parcel in, ClassLoader loader) { 503 name = in.readString(); 504 senderName = in.readString(); 505 type = in.readString(); 506 accountManagerName = in.readString(); 507 providerVersion = in.readInt(); 508 uri = in.readParcelable(null); 509 capabilities = in.readInt(); 510 folderListUri = in.readParcelable(null); 511 fullFolderListUri = in.readParcelable(null); 512 allFolderListUri = in.readParcelable(null); 513 searchUri = in.readParcelable(null); 514 accountFromAddresses = in.readString(); 515 expungeMessageUri = in.readParcelable(null); 516 undoUri = in.readParcelable(null); 517 settingsIntentUri = in.readParcelable(null); 518 helpIntentUri = in.readParcelable(null); 519 sendFeedbackIntentUri = in.readParcelable(null); 520 reauthenticationIntentUri = in.readParcelable(null); 521 syncStatus = in.readInt(); 522 composeIntentUri = in.readParcelable(null); 523 mimeType = in.readString(); 524 recentFolderListUri = in.readParcelable(null); 525 color = in.readInt(); 526 defaultRecentFolderListUri = in.readParcelable(null); 527 manualSyncUri = in.readParcelable(null); 528 viewIntentProxyUri = in.readParcelable(null); 529 accoutCookieQueryUri = in.readParcelable(null); 530 updateSettingsUri = in.readParcelable(null); 531 enableMessageTransforms = in.readInt(); 532 syncAuthority = in.readString(); 533 if (TextUtils.isEmpty(syncAuthority)) { 534 LogUtils.e(LOG_TAG, "Unexpected empty syncAuthority from Parcel"); 535 } 536 quickResponseUri = in.readParcelable(null); 537 final int hasSettings = in.readInt(); 538 if (hasSettings == 0) { 539 LogUtils.e(LOG_TAG, new Throwable(), "Unexpected null settings in Account(Parcel)"); 540 settings = Settings.EMPTY_SETTINGS; 541 } else { 542 settings = in.readParcelable(loader); 543 } 544 } 545 546 @Override 547 public void writeToParcel(Parcel dest, int flags) { 548 dest.writeString(name); 549 dest.writeString(senderName); 550 dest.writeString(type); 551 dest.writeString(accountManagerName); 552 dest.writeInt(providerVersion); 553 dest.writeParcelable(uri, 0); 554 dest.writeInt(capabilities); 555 dest.writeParcelable(folderListUri, 0); 556 dest.writeParcelable(fullFolderListUri, 0); 557 dest.writeParcelable(allFolderListUri, 0); 558 dest.writeParcelable(searchUri, 0); 559 dest.writeString(accountFromAddresses); 560 dest.writeParcelable(expungeMessageUri, 0); 561 dest.writeParcelable(undoUri, 0); 562 dest.writeParcelable(settingsIntentUri, 0); 563 dest.writeParcelable(helpIntentUri, 0); 564 dest.writeParcelable(sendFeedbackIntentUri, 0); 565 dest.writeParcelable(reauthenticationIntentUri, 0); 566 dest.writeInt(syncStatus); 567 dest.writeParcelable(composeIntentUri, 0); 568 dest.writeString(mimeType); 569 dest.writeParcelable(recentFolderListUri, 0); 570 dest.writeInt(color); 571 dest.writeParcelable(defaultRecentFolderListUri, 0); 572 dest.writeParcelable(manualSyncUri, 0); 573 dest.writeParcelable(viewIntentProxyUri, 0); 574 dest.writeParcelable(accoutCookieQueryUri, 0); 575 dest.writeParcelable(updateSettingsUri, 0); 576 dest.writeInt(enableMessageTransforms); 577 dest.writeString(syncAuthority); 578 dest.writeParcelable(quickResponseUri, 0); 579 if (settings == null) { 580 LogUtils.e(LOG_TAG, "unexpected null settings object in writeToParcel"); 581 dest.writeInt(0); 582 } else { 583 dest.writeInt(1); 584 dest.writeParcelable(settings, 0); 585 } 586 } 587 588 @Override 589 public int describeContents() { 590 return 0; 591 } 592 593 @Override 594 public String toString() { 595 // JSON is readable enough. 596 return serialize(); 597 } 598 599 @Override 600 public boolean equals(Object o) { 601 if (o == this) { 602 return true; 603 } 604 605 if ((o == null) || (o.getClass() != this.getClass())) { 606 return false; 607 } 608 609 final Account other = (Account) o; 610 return TextUtils.equals(name, other.name) && 611 TextUtils.equals(senderName, other.senderName) && 612 TextUtils.equals(accountManagerName, other.accountManagerName) && 613 TextUtils.equals(type, other.type) && 614 capabilities == other.capabilities && 615 providerVersion == other.providerVersion && 616 Objects.equal(uri, other.uri) && 617 Objects.equal(folderListUri, other.folderListUri) && 618 Objects.equal(fullFolderListUri, other.fullFolderListUri) && 619 Objects.equal(allFolderListUri, other.allFolderListUri) && 620 Objects.equal(searchUri, other.searchUri) && 621 Objects.equal(accountFromAddresses, other.accountFromAddresses) && 622 Objects.equal(expungeMessageUri, other.expungeMessageUri) && 623 Objects.equal(undoUri, other.undoUri) && 624 Objects.equal(settingsIntentUri, other.settingsIntentUri) && 625 Objects.equal(helpIntentUri, other.helpIntentUri) && 626 Objects.equal(sendFeedbackIntentUri, other.sendFeedbackIntentUri) && 627 Objects.equal(reauthenticationIntentUri, other.reauthenticationIntentUri) && 628 (syncStatus == other.syncStatus) && 629 Objects.equal(composeIntentUri, other.composeIntentUri) && 630 TextUtils.equals(mimeType, other.mimeType) && 631 Objects.equal(recentFolderListUri, other.recentFolderListUri) && 632 color == other.color && 633 Objects.equal(defaultRecentFolderListUri, other.defaultRecentFolderListUri) && 634 Objects.equal(viewIntentProxyUri, other.viewIntentProxyUri) && 635 Objects.equal(accoutCookieQueryUri, other.accoutCookieQueryUri) && 636 Objects.equal(updateSettingsUri, other.updateSettingsUri) && 637 Objects.equal(enableMessageTransforms, other.enableMessageTransforms) && 638 Objects.equal(syncAuthority, other.syncAuthority) && 639 Objects.equal(quickResponseUri, other.quickResponseUri) && 640 Objects.equal(settings, other.settings); 641 } 642 643 /** 644 * Returns true if the two accounts differ in sync or server-side settings. 645 * This is <b>not</b> a replacement for {@link #equals(Object)}. 646 * @param other Account object to compare 647 * @return true if the two accounts differ in sync or server-side settings 648 */ 649 public final boolean settingsDiffer(Account other) { 650 // If the other object doesn't exist, they differ significantly. 651 if (other == null) { 652 return true; 653 } 654 // Check all the server-side settings, the user-side settings and the sync status. 655 return ((this.syncStatus != other.syncStatus) 656 || !Objects.equal(accountFromAddresses, other.accountFromAddresses) 657 || color != other.color 658 || (this.settings.hashCode() != other.settings.hashCode())); 659 } 660 661 @Override 662 public int hashCode() { 663 return Objects.hashCode(name, 664 senderName, 665 accountManagerName, 666 type, 667 capabilities, 668 providerVersion, 669 uri, 670 folderListUri, 671 fullFolderListUri, 672 allFolderListUri, 673 searchUri, 674 accountFromAddresses, 675 expungeMessageUri, 676 undoUri, 677 settingsIntentUri, 678 helpIntentUri, 679 sendFeedbackIntentUri, 680 reauthenticationIntentUri, 681 syncStatus, 682 composeIntentUri, 683 mimeType, 684 recentFolderListUri, 685 color, 686 defaultRecentFolderListUri, 687 viewIntentProxyUri, 688 accoutCookieQueryUri, 689 updateSettingsUri, 690 enableMessageTransforms, 691 syncAuthority, 692 quickResponseUri); 693 } 694 695 /** 696 * Returns whether two Accounts match, as determined by their base URIs. 697 * <p>For a deep object comparison, use {@link #equals(Object)}. 698 * 699 */ 700 public boolean matches(Account other) { 701 return other != null && Objects.equal(uri, other.uri); 702 } 703 704 public List<ReplyFromAccount> getReplyFroms() { 705 706 if (mReplyFroms == null) { 707 mReplyFroms = Lists.newArrayList(); 708 709 // skip if sending is unsupported 710 if (supportsCapability(AccountCapabilities.SENDING_UNAVAILABLE)) { 711 return mReplyFroms; 712 } 713 714 // add the main account address 715 mReplyFroms.add(new ReplyFromAccount(this, uri, getEmailAddress(), getSenderName(), 716 getEmailAddress(), false /* isDefault */, false /* isCustom */)); 717 718 if (!TextUtils.isEmpty(accountFromAddresses)) { 719 try { 720 JSONArray accounts = new JSONArray(accountFromAddresses); 721 722 for (int i = 0, len = accounts.length(); i < len; i++) { 723 final ReplyFromAccount a = ReplyFromAccount.deserialize(this, 724 accounts.getJSONObject(i)); 725 if (a != null) { 726 mReplyFroms.add(a); 727 } 728 } 729 730 } catch (JSONException e) { 731 LogUtils.e(LOG_TAG, e, "Unable to parse accountFromAddresses. name=%s", name); 732 } 733 } 734 } 735 return mReplyFroms; 736 } 737 738 /** 739 * @param fromAddress a raw email address, e.g. "user@domain.com" 740 * @return if the address belongs to this Account (either as the main address or as a 741 * custom-from) 742 */ 743 public boolean ownsFromAddress(String fromAddress) { 744 for (ReplyFromAccount replyFrom : getReplyFroms()) { 745 if (TextUtils.equals(replyFrom.address, fromAddress)) { 746 return true; 747 } 748 } 749 750 return false; 751 } 752 753 public String getEmailAddress() { 754 return accountManagerName; 755 } 756 757 /** 758 * Returns the real name associated with the account, e.g. "John Doe" or null if no such name 759 * has been configured 760 * @return sender name 761 */ 762 public String getSenderName() { 763 return senderName; 764 } 765 766 @SuppressWarnings("hiding") 767 public static final ClassLoaderCreator<Account> CREATOR = new ClassLoaderCreator<Account>() { 768 @Override 769 public Account createFromParcel(Parcel source, ClassLoader loader) { 770 return new Account(source, loader); 771 } 772 773 @Override 774 public Account createFromParcel(Parcel source) { 775 return new Account(source, null); 776 } 777 778 @Override 779 public Account[] newArray(int size) { 780 return new Account[size]; 781 } 782 }; 783 784 /** 785 * Creates a {@link Map} where the column name is the key and the value is the value, which can 786 * be used for populating a {@link MatrixCursor}. 787 */ 788 public Map<String, Object> getValueMap() { 789 // ImmutableMap.Builder does not allow null values 790 final Map<String, Object> map = new HashMap<String, Object>(); 791 792 map.put(AccountColumns._ID, 0); 793 map.put(AccountColumns.NAME, name); 794 map.put(AccountColumns.SENDER_NAME, senderName); 795 map.put(AccountColumns.TYPE, type); 796 map.put(AccountColumns.ACCOUNT_MANAGER_NAME, accountManagerName); 797 map.put(AccountColumns.PROVIDER_VERSION, providerVersion); 798 map.put(AccountColumns.URI, uri); 799 map.put(AccountColumns.CAPABILITIES, capabilities); 800 map.put(AccountColumns.FOLDER_LIST_URI, folderListUri); 801 map.put(AccountColumns.FULL_FOLDER_LIST_URI, fullFolderListUri); 802 map.put(AccountColumns.ALL_FOLDER_LIST_URI, allFolderListUri); 803 map.put(AccountColumns.SEARCH_URI, searchUri); 804 map.put(AccountColumns.ACCOUNT_FROM_ADDRESSES, accountFromAddresses); 805 map.put(AccountColumns.EXPUNGE_MESSAGE_URI, expungeMessageUri); 806 map.put(AccountColumns.UNDO_URI, undoUri); 807 map.put(AccountColumns.SETTINGS_INTENT_URI, settingsIntentUri); 808 map.put(AccountColumns.HELP_INTENT_URI, helpIntentUri); 809 map.put(AccountColumns.SEND_FEEDBACK_INTENT_URI, sendFeedbackIntentUri); 810 map.put(AccountColumns.REAUTHENTICATION_INTENT_URI, reauthenticationIntentUri); 811 map.put(AccountColumns.SYNC_STATUS, syncStatus); 812 map.put(AccountColumns.COMPOSE_URI, composeIntentUri); 813 map.put(AccountColumns.MIME_TYPE, mimeType); 814 map.put(AccountColumns.RECENT_FOLDER_LIST_URI, recentFolderListUri); 815 map.put(AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI, defaultRecentFolderListUri); 816 map.put(AccountColumns.MANUAL_SYNC_URI, manualSyncUri); 817 map.put(AccountColumns.VIEW_INTENT_PROXY_URI, viewIntentProxyUri); 818 map.put(AccountColumns.ACCOUNT_COOKIE_QUERY_URI, accoutCookieQueryUri); 819 map.put(AccountColumns.COLOR, color); 820 map.put(AccountColumns.UPDATE_SETTINGS_URI, updateSettingsUri); 821 map.put(AccountColumns.ENABLE_MESSAGE_TRANSFORMS, enableMessageTransforms); 822 map.put(AccountColumns.SYNC_AUTHORITY, syncAuthority); 823 map.put(AccountColumns.QUICK_RESPONSE_URI, quickResponseUri); 824 settings.getValueMap(map); 825 826 return map; 827 } 828 829 /** 830 * Public object that knows how to construct Accounts given Cursors. 831 */ 832 public final static CursorCreator<Account> FACTORY = new CursorCreator<Account>() { 833 @Override 834 public Account createFromCursor(Cursor c) { 835 return new Account(c); 836 } 837 838 @Override 839 public String toString() { 840 return "Account CursorCreator"; 841 } 842 }; 843} 844