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