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