Account.java revision 5e63e8aae365dc210a0647b7e1c87f76bf652c80
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.database.Cursor; 20import android.net.Uri; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.text.TextUtils; 24 25import com.android.mail.providers.UIProvider.SyncStatus; 26import com.android.mail.utils.LogTag; 27import com.android.mail.utils.LogUtils; 28import com.android.mail.utils.Utils; 29import com.google.common.base.Objects; 30 31import org.json.JSONException; 32import org.json.JSONObject; 33 34public class Account extends android.accounts.Account implements Parcelable { 35 private static final String SETTINGS_KEY = "settings"; 36 37 /** 38 * The version of the UI provider schema from which this account provider 39 * will return results. 40 */ 41 public final int providerVersion; 42 43 /** 44 * The uri to directly access the information for this account. 45 */ 46 public final Uri uri; 47 48 /** 49 * The possible capabilities that this account supports. 50 */ 51 public final int capabilities; 52 53 /** 54 * The content provider uri to return the list of top level folders for this 55 * account. 56 */ 57 public final Uri folderListUri; 58 /** 59 * The content provider uri to return the list of all folders for this 60 * account. 61 */ 62 public Uri fullFolderListUri; 63 /** 64 * The content provider uri that can be queried for search results. 65 */ 66 public final Uri searchUri; 67 68 /** 69 * The custom from addresses for this account or null if there are none. 70 */ 71 public String accountFromAddresses; 72 73 /** 74 * The content provider uri that can be used to save (insert) new draft 75 * messages for this account. NOTE: This might be better to be an update 76 * operation on the messageUri. 77 */ 78 public final Uri saveDraftUri; 79 80 /** 81 * The content provider uri that can be used to send a message for this 82 * account. 83 * NOTE: This might be better to be an update operation on the 84 * messageUri. 85 */ 86 public final Uri sendMessageUri; 87 88 /** 89 * The content provider uri that can be used to expunge message from this 90 * account. NOTE: This might be better to be an update operation on the 91 * messageUri. 92 */ 93 public final Uri expungeMessageUri; 94 95 /** 96 * The content provider uri that can be used to undo the last operation 97 * performed. 98 */ 99 public final Uri undoUri; 100 101 /** 102 * Uri for EDIT intent that will cause the settings screens for this account type to be 103 * shown. 104 */ 105 public final Uri settingsIntentUri; 106 107 /** 108 * Uri for VIEW intent that will cause the help screens for this account type to be 109 * shown. 110 */ 111 public final Uri helpIntentUri; 112 113 /** 114 * Uri for VIEW intent that will cause the send feedback screens for this account type to be 115 * shown. 116 */ 117 public final Uri sendFeedbackIntentUri; 118 119 /** 120 * The sync status of the account 121 */ 122 public final int syncStatus; 123 124 /** 125 * Uri for VIEW intent that will cause the compose screen for this account type to be 126 * shown. 127 */ 128 public final Uri composeIntentUri; 129 130 public final String mimeType; 131 /** 132 * URI for recent folders for this account. 133 */ 134 public final Uri recentFolderListUri; 135 /** 136 * The color used for this account in combined view (Email) 137 */ 138 public final int color; 139 /** 140 * URI for default recent folders for this account, if any. 141 */ 142 public final Uri defaultRecentFolderListUri; 143 /** 144 * Settings object for this account. 145 */ 146 public final Settings settings; 147 148 /** 149 * URI for forcing a manual sync of this account. 150 */ 151 public final Uri manualSyncUri; 152 153 private static final String LOG_TAG = LogTag.getLogTag(); 154 155 /** 156 * Return a serialized String for this account. 157 */ 158 public synchronized String serialize() { 159 JSONObject json = new JSONObject(); 160 try { 161 json.put(UIProvider.AccountColumns.NAME, name); 162 json.put(UIProvider.AccountColumns.TYPE, type); 163 json.put(UIProvider.AccountColumns.PROVIDER_VERSION, providerVersion); 164 json.put(UIProvider.AccountColumns.URI, uri); 165 json.put(UIProvider.AccountColumns.CAPABILITIES, capabilities); 166 json.put(UIProvider.AccountColumns.FOLDER_LIST_URI, folderListUri); 167 json.put(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI, fullFolderListUri); 168 json.put(UIProvider.AccountColumns.SEARCH_URI, searchUri); 169 json.put(UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES, accountFromAddresses); 170 json.put(UIProvider.AccountColumns.SAVE_DRAFT_URI, saveDraftUri); 171 json.put(UIProvider.AccountColumns.SEND_MAIL_URI, sendMessageUri); 172 json.put(UIProvider.AccountColumns.EXPUNGE_MESSAGE_URI, expungeMessageUri); 173 json.put(UIProvider.AccountColumns.UNDO_URI, undoUri); 174 json.put(UIProvider.AccountColumns.SETTINGS_INTENT_URI, settingsIntentUri); 175 json.put(UIProvider.AccountColumns.HELP_INTENT_URI, helpIntentUri); 176 json.put(UIProvider.AccountColumns.SEND_FEEDBACK_INTENT_URI, sendFeedbackIntentUri); 177 json.put(UIProvider.AccountColumns.SYNC_STATUS, syncStatus); 178 json.put(UIProvider.AccountColumns.COMPOSE_URI, composeIntentUri); 179 json.put(UIProvider.AccountColumns.MIME_TYPE, mimeType); 180 json.put(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI, recentFolderListUri); 181 json.put(UIProvider.AccountColumns.COLOR, color); 182 json.put(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI, 183 defaultRecentFolderListUri); 184 json.put(UIProvider.AccountColumns.MANUAL_SYNC_URI, 185 manualSyncUri); 186 if (settings != null) { 187 json.put(SETTINGS_KEY, settings.toJSON()); 188 } 189 } catch (JSONException e) { 190 LogUtils.wtf(LOG_TAG, e, "Could not serialize account with name %s", name); 191 } 192 return json.toString(); 193 } 194 195 /** 196 * Create a new instance of an Account object using a serialized instance created previously 197 * using {@link #serialize()}. This returns null if the serialized instance was invalid or does 198 * not represent a valid account object. 199 * 200 * @param serializedAccount 201 * @return 202 */ 203 public static Account newinstance(String serializedAccount) { 204 // The heavy lifting is done by Account(name, type, serializedAccount). This method 205 // is a wrapper to check for errors and exceptions and return back a null in cases 206 // something breaks. 207 JSONObject json = null; 208 try { 209 json = new JSONObject(serializedAccount); 210 final String name = (String) json.get(UIProvider.AccountColumns.NAME); 211 final String type = (String) json.get(UIProvider.AccountColumns.TYPE); 212 return new Account(name, type, serializedAccount); 213 } catch (JSONException e) { 214 LogUtils.e(LOG_TAG, e, "Could not create an account from this input: \"%s\"", 215 serializedAccount); 216 return null; 217 } 218 } 219 220 /** 221 * Construct a new Account instance from a previously serialized string. This calls 222 * {@link android.accounts.Account#Account(String, String)} with name and type given as the 223 * first two arguments. 224 * 225 * <p> 226 * This is private. Public uses should go through the safe {@link #newinstance(String)} method. 227 * </p> 228 * @param name name of account in {@link android.accounts.Account} 229 * @param type type of account in {@link android.accounts.Account} 230 * @param jsonAccount string obtained from {@link #serialize()} on a valid account. 231 * @throws JSONException 232 */ 233 private Account(String name, String type, String jsonAccount) throws JSONException { 234 super(name, type); 235 final JSONObject json = new JSONObject(jsonAccount); 236 providerVersion = json.getInt(UIProvider.AccountColumns.PROVIDER_VERSION); 237 uri = Uri.parse(json.optString(UIProvider.AccountColumns.URI)); 238 capabilities = json.getInt(UIProvider.AccountColumns.CAPABILITIES); 239 folderListUri = Utils 240 .getValidUri(json.optString(UIProvider.AccountColumns.FOLDER_LIST_URI)); 241 fullFolderListUri = Utils.getValidUri(json 242 .optString(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI)); 243 searchUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.SEARCH_URI)); 244 accountFromAddresses = UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES; 245 saveDraftUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.SAVE_DRAFT_URI)); 246 sendMessageUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.SEND_MAIL_URI)); 247 expungeMessageUri = Utils.getValidUri(json 248 .optString(UIProvider.AccountColumns.EXPUNGE_MESSAGE_URI)); 249 undoUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.UNDO_URI)); 250 settingsIntentUri = Utils.getValidUri(json 251 .optString(UIProvider.AccountColumns.SETTINGS_INTENT_URI)); 252 helpIntentUri = Utils 253 .getValidUri(json.optString(UIProvider.AccountColumns.HELP_INTENT_URI)); 254 sendFeedbackIntentUri = Utils.getValidUri(json 255 .optString(UIProvider.AccountColumns.SEND_FEEDBACK_INTENT_URI)); 256 syncStatus = json.optInt(UIProvider.AccountColumns.SYNC_STATUS); 257 composeIntentUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.COMPOSE_URI)); 258 mimeType = json.optString(UIProvider.AccountColumns.MIME_TYPE); 259 recentFolderListUri = Utils.getValidUri(json 260 .optString(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI)); 261 color = json.optInt(UIProvider.AccountColumns.COLOR, 0); 262 defaultRecentFolderListUri = Utils.getValidUri(json 263 .optString(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI)); 264 manualSyncUri = Utils 265 .getValidUri(json.optString(UIProvider.AccountColumns.MANUAL_SYNC_URI)); 266 267 final Settings jsonSettings = Settings.newInstance(json.optJSONObject(SETTINGS_KEY)); 268 if (jsonSettings != null) { 269 settings = jsonSettings; 270 } else { 271 LogUtils.e(LOG_TAG, new Throwable(), 272 "Unexpected null settings in Account(name, type, jsonAccount)"); 273 settings = Settings.EMPTY_SETTINGS; 274 } 275 } 276 277 public Account(Parcel in) { 278 super(in); 279 providerVersion = in.readInt(); 280 uri = in.readParcelable(null); 281 capabilities = in.readInt(); 282 folderListUri = in.readParcelable(null); 283 fullFolderListUri = in.readParcelable(null); 284 searchUri = in.readParcelable(null); 285 accountFromAddresses = in.readString(); 286 saveDraftUri = in.readParcelable(null); 287 sendMessageUri = in.readParcelable(null); 288 expungeMessageUri = in.readParcelable(null); 289 undoUri = in.readParcelable(null); 290 settingsIntentUri = in.readParcelable(null); 291 helpIntentUri = in.readParcelable(null); 292 sendFeedbackIntentUri = in.readParcelable(null); 293 syncStatus = in.readInt(); 294 composeIntentUri = in.readParcelable(null); 295 mimeType = in.readString(); 296 recentFolderListUri = in.readParcelable(null); 297 color = in.readInt(); 298 defaultRecentFolderListUri = in.readParcelable(null); 299 manualSyncUri = in.readParcelable(null); 300 final String serializedSettings = in.readString(); 301 final Settings parcelSettings = Settings.newInstance(serializedSettings); 302 if (parcelSettings != null) { 303 settings = parcelSettings; 304 } else { 305 LogUtils.e(LOG_TAG, new Throwable(), "Unexpected null settings in Account(Parcel)"); 306 settings = Settings.EMPTY_SETTINGS; 307 } 308 } 309 310 public Account(Cursor cursor) { 311 super(cursor.getString(UIProvider.ACCOUNT_NAME_COLUMN), "unknown"); 312 accountFromAddresses = cursor.getString(UIProvider.ACCOUNT_FROM_ADDRESSES_COLUMN); 313 capabilities = cursor.getInt(UIProvider.ACCOUNT_CAPABILITIES_COLUMN); 314 providerVersion = cursor.getInt(UIProvider.ACCOUNT_PROVIDER_VERISON_COLUMN); 315 uri = Uri.parse(cursor.getString(UIProvider.ACCOUNT_URI_COLUMN)); 316 folderListUri = Uri.parse(cursor.getString(UIProvider.ACCOUNT_FOLDER_LIST_URI_COLUMN)); 317 fullFolderListUri = Utils.getValidUri(cursor 318 .getString(UIProvider.ACCOUNT_FULL_FOLDER_LIST_URI_COLUMN)); 319 searchUri = Utils.getValidUri(cursor.getString(UIProvider.ACCOUNT_SEARCH_URI_COLUMN)); 320 saveDraftUri = Utils 321 .getValidUri(cursor.getString(UIProvider.ACCOUNT_SAVE_DRAFT_URI_COLUMN)); 322 sendMessageUri = Utils.getValidUri(cursor 323 .getString(UIProvider.ACCOUNT_SEND_MESSAGE_URI_COLUMN)); 324 expungeMessageUri = Utils.getValidUri(cursor 325 .getString(UIProvider.ACCOUNT_EXPUNGE_MESSAGE_URI_COLUMN)); 326 undoUri = Utils.getValidUri(cursor.getString(UIProvider.ACCOUNT_UNDO_URI_COLUMN)); 327 settingsIntentUri = Utils.getValidUri(cursor 328 .getString(UIProvider.ACCOUNT_SETTINGS_INTENT_URI_COLUMN)); 329 helpIntentUri = Utils.getValidUri(cursor 330 .getString(UIProvider.ACCOUNT_HELP_INTENT_URI_COLUMN)); 331 sendFeedbackIntentUri = Utils.getValidUri(cursor 332 .getString(UIProvider.ACCOUNT_SEND_FEEDBACK_INTENT_URI_COLUMN)); 333 syncStatus = cursor.getInt(UIProvider.ACCOUNT_SYNC_STATUS_COLUMN); 334 composeIntentUri = Utils.getValidUri(cursor 335 .getString(UIProvider.ACCOUNT_COMPOSE_INTENT_URI_COLUMN)); 336 mimeType = cursor.getString(UIProvider.ACCOUNT_MIME_TYPE_COLUMN); 337 recentFolderListUri = Utils.getValidUri(cursor 338 .getString(UIProvider.ACCOUNT_RECENT_FOLDER_LIST_URI_COLUMN)); 339 color = cursor.getInt(UIProvider.ACCOUNT_COLOR_COLUMN); 340 defaultRecentFolderListUri = Utils.getValidUri(cursor 341 .getString(UIProvider.ACCOUNT_DEFAULT_RECENT_FOLDER_LIST_URI_COLUMN)); 342 manualSyncUri = Utils.getValidUri(cursor 343 .getString(UIProvider.ACCOUNT_MANUAL_SYNC_URI_COLUMN)); 344 settings = new Settings(cursor); 345 } 346 347 /** 348 * Returns an array of all Accounts located at this cursor. This method returns a zero length 349 * array if no account was found. This method does not close the cursor. 350 * @param cursor cursor pointing to the list of accounts 351 * @return the array of all accounts stored at this cursor. 352 */ 353 public static Account[] getAllAccounts(Cursor cursor) { 354 final int initialLength = cursor.getCount(); 355 if (initialLength <= 0 || !cursor.moveToFirst()) { 356 // Return zero length account array rather than null 357 return new Account[0]; 358 } 359 360 Account[] allAccounts = new Account[initialLength]; 361 int i = 0; 362 do { 363 allAccounts[i++] = new Account(cursor); 364 } while (cursor.moveToNext()); 365 // Ensure that the length of the array is accurate 366 assert (i == initialLength); 367 return allAccounts; 368 } 369 370 public boolean supportsCapability(int capability) { 371 return (capabilities & capability) != 0; 372 } 373 374 public boolean isAccountIntialized() { 375 return (syncStatus & SyncStatus.INITIAL_SYNC_NEEDED) != SyncStatus.INITIAL_SYNC_NEEDED; 376 } 377 378 @Override 379 public void writeToParcel(Parcel dest, int flags) { 380 super.writeToParcel(dest, flags); 381 dest.writeInt(providerVersion); 382 dest.writeParcelable(uri, 0); 383 dest.writeInt(capabilities); 384 dest.writeParcelable(folderListUri, 0); 385 dest.writeParcelable(fullFolderListUri, 0); 386 dest.writeParcelable(searchUri, 0); 387 dest.writeString(accountFromAddresses); 388 dest.writeParcelable(saveDraftUri, 0); 389 dest.writeParcelable(sendMessageUri, 0); 390 dest.writeParcelable(expungeMessageUri, 0); 391 dest.writeParcelable(undoUri, 0); 392 dest.writeParcelable(settingsIntentUri, 0); 393 dest.writeParcelable(helpIntentUri, 0); 394 dest.writeParcelable(sendFeedbackIntentUri, 0); 395 dest.writeInt(syncStatus); 396 dest.writeParcelable(composeIntentUri, 0); 397 dest.writeString(mimeType); 398 dest.writeParcelable(recentFolderListUri, 0); 399 dest.writeInt(color); 400 dest.writeParcelable(defaultRecentFolderListUri, 0); 401 dest.writeParcelable(manualSyncUri, 0); 402 if (settings == null) { 403 LogUtils.e(LOG_TAG, "unexpected null settings object in writeToParcel"); 404 } 405 dest.writeString(settings != null ? settings.serialize() : ""); 406 } 407 408 @Override 409 public String toString() { 410 final StringBuilder sb = new StringBuilder(); 411 412 sb.append("name="); 413 sb.append(name); 414 sb.append(",type="); 415 sb.append(type); 416 sb.append(",accountFromAddressUri="); 417 sb.append(accountFromAddresses); 418 sb.append(",capabilities="); 419 sb.append(capabilities); 420 sb.append(",providerVersion="); 421 sb.append(providerVersion); 422 sb.append(",folderListUri="); 423 sb.append(folderListUri); 424 sb.append(",fullFolderListUri="); 425 sb.append(fullFolderListUri); 426 sb.append(",searchUri="); 427 sb.append(searchUri); 428 sb.append(",saveDraftUri="); 429 sb.append(saveDraftUri); 430 sb.append(",sendMessageUri="); 431 sb.append(sendMessageUri); 432 sb.append(",expungeMessageUri="); 433 sb.append(expungeMessageUri); 434 sb.append(",undoUri="); 435 sb.append(undoUri); 436 sb.append(",settingsIntentUri="); 437 sb.append(settingsIntentUri); 438 sb.append(",helpIntentUri="); 439 sb.append(helpIntentUri); 440 sb.append(",sendFeedbackIntentUri="); 441 sb.append(sendFeedbackIntentUri); 442 sb.append(",syncStatus="); 443 sb.append(syncStatus); 444 sb.append(",composeIntentUri="); 445 sb.append(composeIntentUri); 446 sb.append(",mimeType="); 447 sb.append(mimeType); 448 sb.append(",recentFoldersUri="); 449 sb.append(recentFolderListUri); 450 sb.append(",color="); 451 sb.append(Integer.toHexString(color)); 452 sb.append(",defaultRecentFoldersUri="); 453 sb.append(defaultRecentFolderListUri); 454 sb.append(",settings="); 455 sb.append(settings.serialize()); 456 457 return sb.toString(); 458 } 459 460 @Override 461 public boolean equals(Object o) { 462 if (o == this) { 463 return true; 464 } 465 466 if ((o == null) || (o.getClass() != this.getClass())) { 467 return false; 468 } 469 470 final Account other = (Account) o; 471 return TextUtils.equals(name, other.name) && TextUtils.equals(type, other.type) && 472 capabilities == other.capabilities && providerVersion == other.providerVersion && 473 Objects.equal(uri, other.uri) && 474 Objects.equal(folderListUri, other.folderListUri) && 475 Objects.equal(fullFolderListUri, other.fullFolderListUri) && 476 Objects.equal(searchUri, other.searchUri) && 477 Objects.equal(accountFromAddresses, other.accountFromAddresses) && 478 Objects.equal(saveDraftUri, other.saveDraftUri) && 479 Objects.equal(sendMessageUri, other.sendMessageUri) && 480 Objects.equal(expungeMessageUri, other.expungeMessageUri) && 481 Objects.equal(undoUri, other.undoUri) && 482 Objects.equal(settingsIntentUri, other.settingsIntentUri) && 483 Objects.equal(helpIntentUri, other.helpIntentUri) && 484 Objects.equal(sendFeedbackIntentUri, other.sendFeedbackIntentUri) && 485 (syncStatus == other.syncStatus) && 486 Objects.equal(composeIntentUri, other.composeIntentUri) && 487 TextUtils.equals(mimeType, other.mimeType) && 488 Objects.equal(recentFolderListUri, other.recentFolderListUri) && 489 color == other.color && 490 Objects.equal(defaultRecentFolderListUri, other.defaultRecentFolderListUri); 491 } 492 493 @Override 494 public int hashCode() { 495 return super.hashCode() 496 ^ Objects.hashCode(name, type, capabilities, providerVersion, uri, folderListUri, 497 fullFolderListUri, searchUri, accountFromAddresses, saveDraftUri, 498 sendMessageUri, expungeMessageUri, undoUri, settingsIntentUri, 499 helpIntentUri, sendFeedbackIntentUri, syncStatus, composeIntentUri, 500 mimeType, recentFolderListUri, color, defaultRecentFolderListUri); 501 } 502 503 /** 504 * Returns whether two Accounts match, as determined by their base URIs. 505 * <p>For a deep object comparison, use {@link #equals(Object)}. 506 * 507 */ 508 public boolean matches(Account other) { 509 return other != null && Objects.equal(uri, other.uri); 510 } 511 512 @SuppressWarnings("hiding") 513 public static final Creator<Account> CREATOR = new Creator<Account>() { 514 @Override 515 public Account createFromParcel(Parcel source) { 516 return new Account(source); 517 } 518 519 @Override 520 public Account[] newArray(int size) { 521 return new Account[size]; 522 } 523 }; 524 525 /** 526 * Find the position of the given needle in the given array of accounts. 527 * @param haystack the array of accounts to search 528 * @param needle the URI of account to find 529 * @return a position between 0 and haystack.length-1 if an account is found, -1 if not found. 530 */ 531 public static int findPosition(Account[] haystack, Uri needle) { 532 if (haystack != null && haystack.length > 0 && needle != null) { 533 // Need to go through the list of current accounts, and fix the 534 // position. 535 for (int i = 0, size = haystack.length; i < size; ++i) { 536 if (haystack[i].uri.equals(needle)) { 537 LogUtils.d(LOG_TAG, "findPositionOfAccount: Found needle at position %d", i); 538 return i; 539 } 540 } 541 } 542 return -1; 543 } 544} 545