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