Account.java revision 0d0f8b061faae060529af4da339eed2d728ef1bd
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.ContentValues;
20import android.database.Cursor;
21import android.net.Uri;
22import android.os.Parcel;
23import android.os.Parcelable;
24import android.text.TextUtils;
25
26import com.android.mail.providers.UIProvider.AccountCapabilities;
27import com.android.mail.providers.UIProvider.SyncStatus;
28import com.android.mail.utils.LogTag;
29import com.android.mail.utils.LogUtils;
30import com.android.mail.utils.Utils;
31import com.google.common.base.Objects;
32import com.google.common.collect.Lists;
33
34import org.json.JSONArray;
35import org.json.JSONException;
36import org.json.JSONObject;
37
38import java.util.List;
39
40public class Account extends android.accounts.Account implements Parcelable {
41    private static final String SETTINGS_KEY = "settings";
42
43    /**
44     * The version of the UI provider schema from which this account provider
45     * will return results.
46     */
47    public final int providerVersion;
48
49    /**
50     * The uri to directly access the information for this account.
51     */
52    public final Uri uri;
53
54    /**
55     * The possible capabilities that this account supports.
56     */
57    public final int capabilities;
58
59    /**
60     * The content provider uri to return the list of top level folders for this
61     * account.
62     */
63    public final Uri folderListUri;
64    /**
65     * The content provider uri to return the list of all folders for this
66     * account.
67     */
68    public Uri fullFolderListUri;
69    /**
70     * The content provider uri that can be queried for search results.
71     */
72    public final Uri searchUri;
73
74    /**
75     * The custom from addresses for this account or null if there are none.
76     */
77    public String accountFromAddresses;
78
79    /**
80     * The content provider uri that can be used to save (insert) new draft
81     * messages for this account. NOTE: This might be better to be an update
82     * operation on the messageUri.
83     */
84    @Deprecated
85    public final Uri saveDraftUri;
86
87    /**
88     * The content provider uri that can be used to send a message for this
89     * account.
90     * NOTE: This might be better to be an update operation on the
91     * messageUri.
92     */
93    @Deprecated
94    public final Uri sendMessageUri;
95
96    /**
97     * The content provider uri that can be used to expunge message from this
98     * account. NOTE: This might be better to be an update operation on the
99     * messageUri.
100     */
101    public final Uri expungeMessageUri;
102
103    /**
104     * The content provider uri that can be used to undo the last operation
105     * performed.
106     */
107    public final Uri undoUri;
108
109    /**
110     * Uri for EDIT intent that will cause the settings screens for this account type to be
111     * shown.
112     */
113    public final Uri settingsIntentUri;
114
115    /**
116     * Uri for VIEW intent that will cause the help screens for this account type to be
117     * shown.
118     */
119    public final Uri helpIntentUri;
120
121    /**
122     * Uri for VIEW intent that will cause the send feedback screens for this account type to be
123     * shown.
124     */
125    public final Uri sendFeedbackIntentUri;
126
127    /**
128     * Uri for VIEW intent that will cause the reauthentication screen for this account to be
129     * shown.
130     */
131    public final Uri reauthenticationIntentUri;
132
133    /**
134     * The sync status of the account
135     */
136    public final int syncStatus;
137
138    /**
139     * Uri for VIEW intent that will cause the compose screen for this account type to be
140     * shown.
141     */
142    public final Uri composeIntentUri;
143
144    public final String mimeType;
145    /**
146     * URI for recent folders for this account.
147     */
148    public final Uri recentFolderListUri;
149    /**
150     * The color used for this account in combined view (Email)
151     */
152    public final int color;
153    /**
154     * URI for default recent folders for this account, if any.
155     */
156    public final Uri defaultRecentFolderListUri;
157    /**
158     * Settings object for this account.
159     */
160    public final Settings settings;
161
162    /**
163     * URI for forcing a manual sync of this account.
164     */
165    public final Uri manualSyncUri;
166
167    /**
168     * URI for account type specific supplementary account info on outgoing links, if any.
169     */
170    public final Uri viewIntentProxyUri;
171
172    /**
173     * URI for querying for the account cookies to be used when displaying inline content in a
174     * conversation
175     */
176    public final Uri accoutCookieQueryUri;
177
178    /**
179     * URI to be used with an update() ContentResolver call with a {@link ContentValues} object
180     * where the keys are from the {@link AccountColumns.SettingsColumns}, and the values are the
181     * new values.
182     */
183    public final Uri updateSettingsUri;
184
185    /**
186     * Transient cache of parsed {@link #accountFromAddresses}, plus an entry for the main account
187     * address.
188     */
189    private transient List<ReplyFromAccount> mReplyFroms;
190
191    private static final String LOG_TAG = LogTag.getLogTag();
192
193    /**
194     * Return a serialized String for this account.
195     */
196    public synchronized String serialize() {
197        JSONObject json = new JSONObject();
198        try {
199            json.put(UIProvider.AccountColumns.NAME, name);
200            json.put(UIProvider.AccountColumns.TYPE, type);
201            json.put(UIProvider.AccountColumns.PROVIDER_VERSION, providerVersion);
202            json.put(UIProvider.AccountColumns.URI, uri);
203            json.put(UIProvider.AccountColumns.CAPABILITIES, capabilities);
204            json.put(UIProvider.AccountColumns.FOLDER_LIST_URI, folderListUri);
205            json.put(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI, fullFolderListUri);
206            json.put(UIProvider.AccountColumns.SEARCH_URI, searchUri);
207            json.put(UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES, accountFromAddresses);
208            json.put(UIProvider.AccountColumns.SAVE_DRAFT_URI, saveDraftUri);
209            json.put(UIProvider.AccountColumns.SEND_MAIL_URI, sendMessageUri);
210            json.put(UIProvider.AccountColumns.EXPUNGE_MESSAGE_URI, expungeMessageUri);
211            json.put(UIProvider.AccountColumns.UNDO_URI, undoUri);
212            json.put(UIProvider.AccountColumns.SETTINGS_INTENT_URI, settingsIntentUri);
213            json.put(UIProvider.AccountColumns.HELP_INTENT_URI, helpIntentUri);
214            json.put(UIProvider.AccountColumns.SEND_FEEDBACK_INTENT_URI, sendFeedbackIntentUri);
215            json.put(UIProvider.AccountColumns.REAUTHENTICATION_INTENT_URI,
216                    reauthenticationIntentUri);
217            json.put(UIProvider.AccountColumns.SYNC_STATUS, syncStatus);
218            json.put(UIProvider.AccountColumns.COMPOSE_URI, composeIntentUri);
219            json.put(UIProvider.AccountColumns.MIME_TYPE, mimeType);
220            json.put(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI, recentFolderListUri);
221            json.put(UIProvider.AccountColumns.COLOR, color);
222            json.put(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI,
223                    defaultRecentFolderListUri);
224            json.put(UIProvider.AccountColumns.MANUAL_SYNC_URI,
225                    manualSyncUri);
226            json.put(UIProvider.AccountColumns.VIEW_INTENT_PROXY_URI,
227                    viewIntentProxyUri);
228            json.put(UIProvider.AccountColumns.ACCOUNT_COOKIE_QUERY_URI, accoutCookieQueryUri);
229            json.put(UIProvider.AccountColumns.UPDATE_SETTINGS_URI, updateSettingsUri);
230            if (settings != null) {
231                json.put(SETTINGS_KEY, settings.toJSON());
232            }
233        } catch (JSONException e) {
234            LogUtils.wtf(LOG_TAG, e, "Could not serialize account with name %s", name);
235        }
236        return json.toString();
237    }
238
239    /**
240     * Create a new instance of an Account object using a serialized instance created previously
241     * using {@link #serialize()}. This returns null if the serialized instance was invalid or does
242     * not represent a valid account object.
243     *
244     * @param serializedAccount
245     * @return
246     */
247    public static Account newinstance(String serializedAccount) {
248        // The heavy lifting is done by Account(name, type, serializedAccount). This method
249        // is a wrapper to check for errors and exceptions and return back a null in cases
250        // something breaks.
251        JSONObject json = null;
252        try {
253            json = new JSONObject(serializedAccount);
254            final String name = (String) json.get(UIProvider.AccountColumns.NAME);
255            final String type = (String) json.get(UIProvider.AccountColumns.TYPE);
256            return new Account(name, type, serializedAccount);
257        } catch (JSONException e) {
258            LogUtils.e(LOG_TAG, e, "Could not create an account from this input: \"%s\"",
259                    serializedAccount);
260            return null;
261        }
262    }
263
264    /**
265     * Construct a new Account instance from a previously serialized string. This calls
266     * {@link android.accounts.Account#Account(String, String)} with name and type given as the
267     * first two arguments.
268     *
269     * <p>
270     * This is private. Public uses should go through the safe {@link #newinstance(String)} method.
271     * </p>
272     * @param name name of account in {@link android.accounts.Account}
273     * @param type type of account in {@link android.accounts.Account}
274     * @param jsonAccount string obtained from {@link #serialize()} on a valid account.
275     * @throws JSONException
276     */
277    private Account(String name, String type, String jsonAccount) throws JSONException {
278        super(name, type);
279        final JSONObject json = new JSONObject(jsonAccount);
280        providerVersion = json.getInt(UIProvider.AccountColumns.PROVIDER_VERSION);
281        uri = Uri.parse(json.optString(UIProvider.AccountColumns.URI));
282        capabilities = json.getInt(UIProvider.AccountColumns.CAPABILITIES);
283        folderListUri = Utils
284                .getValidUri(json.optString(UIProvider.AccountColumns.FOLDER_LIST_URI));
285        fullFolderListUri = Utils.getValidUri(json
286                .optString(UIProvider.AccountColumns.FULL_FOLDER_LIST_URI));
287        searchUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.SEARCH_URI));
288        accountFromAddresses = json.optString(UIProvider.AccountColumns.ACCOUNT_FROM_ADDRESSES,
289                "");
290        saveDraftUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.SAVE_DRAFT_URI));
291        sendMessageUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.SEND_MAIL_URI));
292        expungeMessageUri = Utils.getValidUri(json
293                .optString(UIProvider.AccountColumns.EXPUNGE_MESSAGE_URI));
294        undoUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.UNDO_URI));
295        settingsIntentUri = Utils.getValidUri(json
296                .optString(UIProvider.AccountColumns.SETTINGS_INTENT_URI));
297        helpIntentUri = Utils
298                .getValidUri(json.optString(UIProvider.AccountColumns.HELP_INTENT_URI));
299        sendFeedbackIntentUri = Utils.getValidUri(json
300                .optString(UIProvider.AccountColumns.SEND_FEEDBACK_INTENT_URI));
301        reauthenticationIntentUri = Utils.getValidUri(
302                json.optString(UIProvider.AccountColumns.REAUTHENTICATION_INTENT_URI));
303        syncStatus = json.optInt(UIProvider.AccountColumns.SYNC_STATUS);
304        composeIntentUri = Utils.getValidUri(json.optString(UIProvider.AccountColumns.COMPOSE_URI));
305        mimeType = json.optString(UIProvider.AccountColumns.MIME_TYPE);
306        recentFolderListUri = Utils.getValidUri(json
307                .optString(UIProvider.AccountColumns.RECENT_FOLDER_LIST_URI));
308        color = json.optInt(UIProvider.AccountColumns.COLOR, 0);
309        defaultRecentFolderListUri = Utils.getValidUri(json
310                .optString(UIProvider.AccountColumns.DEFAULT_RECENT_FOLDER_LIST_URI));
311        manualSyncUri = Utils
312                .getValidUri(json.optString(UIProvider.AccountColumns.MANUAL_SYNC_URI));
313        viewIntentProxyUri = Utils
314                .getValidUri(json.optString(UIProvider.AccountColumns.VIEW_INTENT_PROXY_URI));
315        accoutCookieQueryUri = Utils.getValidUri(
316                json.optString(UIProvider.AccountColumns.ACCOUNT_COOKIE_QUERY_URI));
317        updateSettingsUri = Utils.getValidUri(
318                json.optString(UIProvider.AccountColumns.UPDATE_SETTINGS_URI));
319
320        final Settings jsonSettings = Settings.newInstance(json.optJSONObject(SETTINGS_KEY));
321        if (jsonSettings != null) {
322            settings = jsonSettings;
323        } else {
324            LogUtils.e(LOG_TAG, new Throwable(),
325                    "Unexpected null settings in Account(name, type, jsonAccount)");
326            settings = Settings.EMPTY_SETTINGS;
327        }
328    }
329
330    public Account(Parcel in) {
331        super(in);
332        providerVersion = in.readInt();
333        uri = in.readParcelable(null);
334        capabilities = in.readInt();
335        folderListUri = in.readParcelable(null);
336        fullFolderListUri = in.readParcelable(null);
337        searchUri = in.readParcelable(null);
338        accountFromAddresses = in.readString();
339        saveDraftUri = in.readParcelable(null);
340        sendMessageUri = in.readParcelable(null);
341        expungeMessageUri = in.readParcelable(null);
342        undoUri = in.readParcelable(null);
343        settingsIntentUri = in.readParcelable(null);
344        helpIntentUri = in.readParcelable(null);
345        sendFeedbackIntentUri = in.readParcelable(null);
346        reauthenticationIntentUri = in.readParcelable(null);
347        syncStatus = in.readInt();
348        composeIntentUri = in.readParcelable(null);
349        mimeType = in.readString();
350        recentFolderListUri = in.readParcelable(null);
351        color = in.readInt();
352        defaultRecentFolderListUri = in.readParcelable(null);
353        manualSyncUri = in.readParcelable(null);
354        viewIntentProxyUri = in.readParcelable(null);
355        accoutCookieQueryUri = in.readParcelable(null);
356        updateSettingsUri = in.readParcelable(null);
357        final String serializedSettings = in.readString();
358        final Settings parcelSettings = Settings.newInstance(serializedSettings);
359        if (parcelSettings != null) {
360            settings = parcelSettings;
361        } else {
362            LogUtils.e(LOG_TAG, new Throwable(), "Unexpected null settings in Account(Parcel)");
363            settings = Settings.EMPTY_SETTINGS;
364        }
365    }
366
367    public Account(Cursor cursor) {
368        super(cursor.getString(UIProvider.ACCOUNT_NAME_COLUMN), "unknown");
369        accountFromAddresses = cursor.getString(UIProvider.ACCOUNT_FROM_ADDRESSES_COLUMN);
370        capabilities = cursor.getInt(UIProvider.ACCOUNT_CAPABILITIES_COLUMN);
371        providerVersion = cursor.getInt(UIProvider.ACCOUNT_PROVIDER_VERISON_COLUMN);
372        uri = Uri.parse(cursor.getString(UIProvider.ACCOUNT_URI_COLUMN));
373        folderListUri = Uri.parse(cursor.getString(UIProvider.ACCOUNT_FOLDER_LIST_URI_COLUMN));
374        fullFolderListUri = Utils.getValidUri(cursor
375                .getString(UIProvider.ACCOUNT_FULL_FOLDER_LIST_URI_COLUMN));
376        searchUri = Utils.getValidUri(cursor.getString(UIProvider.ACCOUNT_SEARCH_URI_COLUMN));
377        saveDraftUri = Utils
378                .getValidUri(cursor.getString(UIProvider.ACCOUNT_SAVE_DRAFT_URI_COLUMN));
379        sendMessageUri = Utils.getValidUri(cursor
380                .getString(UIProvider.ACCOUNT_SEND_MESSAGE_URI_COLUMN));
381        expungeMessageUri = Utils.getValidUri(cursor
382                .getString(UIProvider.ACCOUNT_EXPUNGE_MESSAGE_URI_COLUMN));
383        undoUri = Utils.getValidUri(cursor.getString(UIProvider.ACCOUNT_UNDO_URI_COLUMN));
384        settingsIntentUri = Utils.getValidUri(cursor
385                .getString(UIProvider.ACCOUNT_SETTINGS_INTENT_URI_COLUMN));
386        helpIntentUri = Utils.getValidUri(cursor
387                .getString(UIProvider.ACCOUNT_HELP_INTENT_URI_COLUMN));
388        sendFeedbackIntentUri = Utils.getValidUri(cursor
389                .getString(UIProvider.ACCOUNT_SEND_FEEDBACK_INTENT_URI_COLUMN));
390        reauthenticationIntentUri = Utils.getValidUri(
391                cursor.getString(UIProvider.ACCOUNT_REAUTHENTICATION_INTENT_URI_COLUMN));
392        syncStatus = cursor.getInt(UIProvider.ACCOUNT_SYNC_STATUS_COLUMN);
393        composeIntentUri = Utils.getValidUri(cursor
394                .getString(UIProvider.ACCOUNT_COMPOSE_INTENT_URI_COLUMN));
395        mimeType = cursor.getString(UIProvider.ACCOUNT_MIME_TYPE_COLUMN);
396        recentFolderListUri = Utils.getValidUri(cursor
397                .getString(UIProvider.ACCOUNT_RECENT_FOLDER_LIST_URI_COLUMN));
398        color = cursor.getInt(UIProvider.ACCOUNT_COLOR_COLUMN);
399        defaultRecentFolderListUri = Utils.getValidUri(cursor
400                .getString(UIProvider.ACCOUNT_DEFAULT_RECENT_FOLDER_LIST_URI_COLUMN));
401        manualSyncUri = Utils.getValidUri(cursor
402                .getString(UIProvider.ACCOUNT_MANUAL_SYNC_URI_COLUMN));
403        viewIntentProxyUri = Utils.getValidUri(cursor
404                .getString(UIProvider.ACCOUNT_VIEW_INTENT_PROXY_URI_COLUMN));
405        accoutCookieQueryUri = Utils.getValidUri(cursor
406                .getString(UIProvider.ACCOUNT_COOKIE_QUERY_URI_COLUMN));
407        updateSettingsUri = Utils.getValidUri(cursor
408                .getString(UIProvider.ACCOUNT_UPDATE_SETTINGS_URI_COLUMN));
409        settings = new Settings(cursor);
410    }
411
412    /**
413     * Returns an array of all Accounts located at this cursor. This method returns a zero length
414     * array if no account was found.  This method does not close the cursor.
415     * @param cursor cursor pointing to the list of accounts
416     * @return the array of all accounts stored at this cursor.
417     */
418    public static Account[] getAllAccounts(Cursor cursor) {
419        final int initialLength = cursor.getCount();
420        if (initialLength <= 0 || !cursor.moveToFirst()) {
421            // Return zero length account array rather than null
422            return new Account[0];
423        }
424
425        final Account[] allAccounts = new Account[initialLength];
426        int i = 0;
427        do {
428            allAccounts[i++] = new Account(cursor);
429        } while (cursor.moveToNext());
430        // Ensure that the length of the array is accurate
431        assert (i == initialLength);
432        return allAccounts;
433    }
434
435    public boolean supportsCapability(int capability) {
436        return (capabilities & capability) != 0;
437    }
438
439    public boolean isAccountSyncRequired() {
440        return (syncStatus & SyncStatus.INITIAL_SYNC_NEEDED) == SyncStatus.INITIAL_SYNC_NEEDED;
441    }
442
443    public boolean isAccountInitializationRequired() {
444        return (syncStatus & SyncStatus.ACCOUNT_INITIALIZATION_REQUIRED) ==
445                SyncStatus.ACCOUNT_INITIALIZATION_REQUIRED;
446    }
447
448    /**
449     * Returns true when when the UI provider has indicated that the account has been initialized,
450     * and sync is not required.
451     */
452    public boolean isAccountReady() {
453        return !isAccountInitializationRequired() && !isAccountSyncRequired();
454    }
455
456    @Override
457    public void writeToParcel(Parcel dest, int flags) {
458        super.writeToParcel(dest, flags);
459        dest.writeInt(providerVersion);
460        dest.writeParcelable(uri, 0);
461        dest.writeInt(capabilities);
462        dest.writeParcelable(folderListUri, 0);
463        dest.writeParcelable(fullFolderListUri, 0);
464        dest.writeParcelable(searchUri, 0);
465        dest.writeString(accountFromAddresses);
466        dest.writeParcelable(saveDraftUri, 0);
467        dest.writeParcelable(sendMessageUri, 0);
468        dest.writeParcelable(expungeMessageUri, 0);
469        dest.writeParcelable(undoUri, 0);
470        dest.writeParcelable(settingsIntentUri, 0);
471        dest.writeParcelable(helpIntentUri, 0);
472        dest.writeParcelable(sendFeedbackIntentUri, 0);
473        dest.writeParcelable(reauthenticationIntentUri, 0);
474        dest.writeInt(syncStatus);
475        dest.writeParcelable(composeIntentUri, 0);
476        dest.writeString(mimeType);
477        dest.writeParcelable(recentFolderListUri, 0);
478        dest.writeInt(color);
479        dest.writeParcelable(defaultRecentFolderListUri, 0);
480        dest.writeParcelable(manualSyncUri, 0);
481        dest.writeParcelable(viewIntentProxyUri, 0);
482        dest.writeParcelable(accoutCookieQueryUri, 0);
483        dest.writeParcelable(updateSettingsUri, 0);
484        if (settings == null) {
485            LogUtils.e(LOG_TAG, "unexpected null settings object in writeToParcel");
486        }
487        dest.writeString(settings != null ? settings.serialize() : "");
488    }
489
490    @Override
491    public String toString() {
492        final StringBuilder sb = new StringBuilder();
493
494        sb.append("name=");
495        sb.append(name);
496        sb.append(",type=");
497        sb.append(type);
498        sb.append(",accountFromAddressUri=");
499        sb.append(accountFromAddresses);
500        sb.append(",capabilities=");
501        sb.append(capabilities);
502        sb.append(",providerVersion=");
503        sb.append(providerVersion);
504        sb.append(",folderListUri=");
505        sb.append(folderListUri);
506        sb.append(",fullFolderListUri=");
507        sb.append(fullFolderListUri);
508        sb.append(",searchUri=");
509        sb.append(searchUri);
510        sb.append(",saveDraftUri=");
511        sb.append(saveDraftUri);
512        sb.append(",sendMessageUri=");
513        sb.append(sendMessageUri);
514        sb.append(",expungeMessageUri=");
515        sb.append(expungeMessageUri);
516        sb.append(",undoUri=");
517        sb.append(undoUri);
518        sb.append(",settingsIntentUri=");
519        sb.append(settingsIntentUri);
520        sb.append(",helpIntentUri=");
521        sb.append(helpIntentUri);
522        sb.append(",sendFeedbackIntentUri=");
523        sb.append(sendFeedbackIntentUri);
524        sb.append(",reauthenticationIntentUri=");
525        sb.append(reauthenticationIntentUri);
526        sb.append(",syncStatus=");
527        sb.append(syncStatus);
528        sb.append(",composeIntentUri=");
529        sb.append(composeIntentUri);
530        sb.append(",mimeType=");
531        sb.append(mimeType);
532        sb.append(",recentFoldersUri=");
533        sb.append(recentFolderListUri);
534        sb.append(",color=");
535        sb.append(Integer.toHexString(color));
536        sb.append(",defaultRecentFoldersUri=");
537        sb.append(defaultRecentFolderListUri);
538        sb.append(",settings=");
539        sb.append(settings.serialize());
540
541        return sb.toString();
542    }
543
544    @Override
545    public boolean equals(Object o) {
546        if (o == this) {
547            return true;
548        }
549
550        if ((o == null) || (o.getClass() != this.getClass())) {
551            return false;
552        }
553
554        final Account other = (Account) o;
555        return TextUtils.equals(name, other.name) && TextUtils.equals(type, other.type) &&
556                capabilities == other.capabilities && providerVersion == other.providerVersion &&
557                Objects.equal(uri, other.uri) &&
558                Objects.equal(folderListUri, other.folderListUri) &&
559                Objects.equal(fullFolderListUri, other.fullFolderListUri) &&
560                Objects.equal(searchUri, other.searchUri) &&
561                Objects.equal(accountFromAddresses, other.accountFromAddresses) &&
562                Objects.equal(saveDraftUri, other.saveDraftUri) &&
563                Objects.equal(sendMessageUri, other.sendMessageUri) &&
564                Objects.equal(expungeMessageUri, other.expungeMessageUri) &&
565                Objects.equal(undoUri, other.undoUri) &&
566                Objects.equal(settingsIntentUri, other.settingsIntentUri) &&
567                Objects.equal(helpIntentUri, other.helpIntentUri) &&
568                Objects.equal(sendFeedbackIntentUri, other.sendFeedbackIntentUri) &&
569                Objects.equal(reauthenticationIntentUri, other.reauthenticationIntentUri) &&
570                (syncStatus == other.syncStatus) &&
571                Objects.equal(composeIntentUri, other.composeIntentUri) &&
572                TextUtils.equals(mimeType, other.mimeType) &&
573                Objects.equal(recentFolderListUri, other.recentFolderListUri) &&
574                color == other.color &&
575                Objects.equal(defaultRecentFolderListUri, other.defaultRecentFolderListUri) &&
576                Objects.equal(viewIntentProxyUri, other.viewIntentProxyUri) &&
577                Objects.equal(accoutCookieQueryUri, other.accoutCookieQueryUri) &&
578                Objects.equal(updateSettingsUri, other.updateSettingsUri) &&
579                Objects.equal(settings, other.settings);
580    }
581
582    /**
583     * Returns true if the two accounts differ in sync or server-side settings.
584     * This is <b>not</b> a replacement for {@link #equals(Object)}.
585     * @param other
586     * @return
587     */
588    public final boolean settingsDiffer(Account other) {
589        // If the other object doesn't exist, they differ significantly.
590        if (other == null) {
591            return true;
592        }
593        // Check all the server-side settings, the user-side settings and the sync status.
594        return ((this.syncStatus != other.syncStatus)
595                || !Objects.equal(accountFromAddresses, other.accountFromAddresses)
596                || color != other.color
597                || (this.settings.hashCode() != other.settings.hashCode()));
598    }
599
600    @Override
601    public int hashCode() {
602        return super.hashCode()
603                ^ Objects.hashCode(name, type, capabilities, providerVersion, uri, folderListUri,
604                        fullFolderListUri, searchUri, accountFromAddresses, saveDraftUri,
605                        sendMessageUri, expungeMessageUri, undoUri, settingsIntentUri,
606                        helpIntentUri, sendFeedbackIntentUri, reauthenticationIntentUri, syncStatus,
607                        composeIntentUri, mimeType, recentFolderListUri, color,
608                        defaultRecentFolderListUri, viewIntentProxyUri, accoutCookieQueryUri,
609                        updateSettingsUri);
610    }
611
612    /**
613     * Returns whether two Accounts match, as determined by their base URIs.
614     * <p>For a deep object comparison, use {@link #equals(Object)}.
615     *
616     */
617    public boolean matches(Account other) {
618        return other != null && Objects.equal(uri, other.uri);
619    }
620
621    public List<ReplyFromAccount> getReplyFroms() {
622
623        if (mReplyFroms == null) {
624            mReplyFroms = Lists.newArrayList();
625
626            // skip if sending is unsupported
627            if (supportsCapability(AccountCapabilities.SENDING_UNAVAILABLE)) {
628                return mReplyFroms;
629            }
630
631            // add the main account address
632            mReplyFroms.add(new ReplyFromAccount(this, uri, name, name, name,
633                    false /* isDefault */, false /* isCustom */));
634
635            if (!TextUtils.isEmpty(accountFromAddresses)) {
636                try {
637                    JSONArray accounts = new JSONArray(accountFromAddresses);
638
639                    for (int i = 0, len = accounts.length(); i < len; i++) {
640                        final ReplyFromAccount a = ReplyFromAccount.deserialize(this,
641                                accounts.getJSONObject(i));
642                        if (a != null) {
643                            mReplyFroms.add(a);
644                        }
645                    }
646
647                } catch (JSONException e) {
648                    LogUtils.e(LOG_TAG, e, "Unable to parse accountFromAddresses. name=%s", name);
649                }
650            }
651        }
652        return mReplyFroms;
653    }
654
655    /**
656     * @param fromAddress a raw email address, e.g. "user@domain.com"
657     * @return if the address belongs to this Account (either as the main address or as a
658     * custom-from)
659     */
660    public boolean ownsFromAddress(String fromAddress) {
661        for (ReplyFromAccount replyFrom : getReplyFroms()) {
662            if (TextUtils.equals(replyFrom.address, fromAddress)) {
663                return true;
664            }
665        }
666
667        return false;
668    }
669
670    @SuppressWarnings("hiding")
671    public static final Creator<Account> CREATOR = new Creator<Account>() {
672        @Override
673         public Account createFromParcel(Parcel source) {
674            return new Account(source);
675        }
676
677        @Override
678        public Account[] newArray(int size) {
679            return new Account[size];
680        }
681    };
682
683    /**
684     * Find the position of the given needle in the given array of accounts.
685     * @param haystack the array of accounts to search
686     * @param needle the URI of account to find
687     * @return a position between 0 and haystack.length-1 if an account is found, -1 if not found.
688     */
689    public static int findPosition(Account[] haystack, Uri needle) {
690        if (haystack != null && haystack.length > 0 && needle != null) {
691            // Need to go through the list of current accounts, and fix the
692            // position.
693            for (int i = 0, size = haystack.length; i < size; ++i) {
694                if (haystack[i].uri.equals(needle)) {
695                    LogUtils.d(LOG_TAG, "findPositionOfAccount: Found needle at position %d", i);
696                    return i;
697                }
698            }
699        }
700        return -1;
701    }
702}
703