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