Mailbox.java revision 03cd72805dab0379ed255d151f1c17cc60655fc3
1/*
2 * Copyright (C) 2009 The Android Open Source Project
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
17
18package com.android.emailcommon.provider;
19
20import android.content.ContentUris;
21import android.content.ContentValues;
22import android.content.Context;
23import android.database.Cursor;
24import android.net.Uri;
25import android.os.Parcel;
26import android.os.Parcelable;
27import android.util.Log;
28
29import com.android.emailcommon.Logging;
30import com.android.emailcommon.provider.EmailContent.MailboxColumns;
31import com.android.emailcommon.provider.EmailContent.SyncColumns;
32import com.android.emailcommon.utility.Utility;
33
34public class Mailbox extends EmailContent implements SyncColumns, MailboxColumns, Parcelable {
35    public static final String TABLE_NAME = "Mailbox";
36    @SuppressWarnings("hiding")
37    public static final Uri CONTENT_URI = Uri.parse(EmailContent.CONTENT_URI + "/mailbox");
38    public static final Uri ADD_TO_FIELD_URI =
39        Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdAddToField");
40    public static final Uri FROM_ACCOUNT_AND_TYPE_URI =
41        Uri.parse(EmailContent.CONTENT_URI + "/mailboxIdFromAccountAndType");
42
43    public String mDisplayName;
44    public String mServerId;
45    public String mParentServerId;
46    public long mParentKey;
47    public long mAccountKey;
48    public int mType;
49    public int mDelimiter;
50    public String mSyncKey;
51    public int mSyncLookback;
52    public int mSyncInterval;
53    public long mSyncTime;
54    public boolean mFlagVisible = true;
55    public int mFlags;
56    public int mVisibleLimit;
57    public String mSyncStatus;
58    public long mLastSeenMessageKey;
59    public long mLastTouchedTime;
60
61    public static final int CONTENT_ID_COLUMN = 0;
62    public static final int CONTENT_DISPLAY_NAME_COLUMN = 1;
63    public static final int CONTENT_SERVER_ID_COLUMN = 2;
64    public static final int CONTENT_PARENT_SERVER_ID_COLUMN = 3;
65    public static final int CONTENT_ACCOUNT_KEY_COLUMN = 4;
66    public static final int CONTENT_TYPE_COLUMN = 5;
67    public static final int CONTENT_DELIMITER_COLUMN = 6;
68    public static final int CONTENT_SYNC_KEY_COLUMN = 7;
69    public static final int CONTENT_SYNC_LOOKBACK_COLUMN = 8;
70    public static final int CONTENT_SYNC_INTERVAL_COLUMN = 9;
71    public static final int CONTENT_SYNC_TIME_COLUMN = 10;
72    public static final int CONTENT_FLAG_VISIBLE_COLUMN = 11;
73    public static final int CONTENT_FLAGS_COLUMN = 12;
74    public static final int CONTENT_VISIBLE_LIMIT_COLUMN = 13;
75    public static final int CONTENT_SYNC_STATUS_COLUMN = 14;
76    public static final int CONTENT_PARENT_KEY_COLUMN = 15;
77    public static final int CONTENT_LAST_SEEN_MESSAGE_KEY_COLUMN = 16;
78    public static final int CONTENT_LAST_TOUCHED_TIME_COLUMN = 17;
79
80    /**
81     * <em>NOTE</em>: If fields are added or removed, the method {@link #getHashes()}
82     * MUST be updated.
83     */
84    public static final String[] CONTENT_PROJECTION = new String[] {
85        RECORD_ID, MailboxColumns.DISPLAY_NAME, MailboxColumns.SERVER_ID,
86        MailboxColumns.PARENT_SERVER_ID, MailboxColumns.ACCOUNT_KEY, MailboxColumns.TYPE,
87        MailboxColumns.DELIMITER, MailboxColumns.SYNC_KEY, MailboxColumns.SYNC_LOOKBACK,
88        MailboxColumns.SYNC_INTERVAL, MailboxColumns.SYNC_TIME,
89        MailboxColumns.FLAG_VISIBLE, MailboxColumns.FLAGS, MailboxColumns.VISIBLE_LIMIT,
90        MailboxColumns.SYNC_STATUS, MailboxColumns.PARENT_KEY,
91        MailboxColumns.LAST_SEEN_MESSAGE_KEY, MailboxColumns.LAST_TOUCHED_TIME,
92    };
93
94    private static final String ACCOUNT_AND_MAILBOX_TYPE_SELECTION =
95            MailboxColumns.ACCOUNT_KEY + " =? AND " +
96            MailboxColumns.TYPE + " =?";
97    private static final String MAILBOX_TYPE_SELECTION =
98            MailboxColumns.TYPE + " =?";
99    /** Selection by server pathname for a given account */
100    public static final String PATH_AND_ACCOUNT_SELECTION =
101        MailboxColumns.SERVER_ID + "=? and " + MailboxColumns.ACCOUNT_KEY + "=?";
102
103    private static final String[] MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION = new String [] {
104            "sum(" + MailboxColumns.UNREAD_COUNT + ")"
105            };
106    private static final int UNREAD_COUNT_COUNT_COLUMN = 0;
107    private static final String[] MAILBOX_SUM_OF_MESSAGE_COUNT_PROJECTION = new String [] {
108            "sum(" + MailboxColumns.MESSAGE_COUNT + ")"
109            };
110    private static final int MESSAGE_COUNT_COUNT_COLUMN = 0;
111
112    private static final String[] MAILBOX_TYPE_PROJECTION = new String [] {
113            MailboxColumns.TYPE
114            };
115    private static final int MAILBOX_TYPE_TYPE_COLUMN = 0;
116
117    private static final String[] MAILBOX_DISPLAY_NAME_PROJECTION = new String [] {
118            MailboxColumns.DISPLAY_NAME
119            };
120    private static final int MAILBOX_DISPLAY_NAME_COLUMN = 0;
121
122    public static final long NO_MAILBOX = -1;
123
124    // Sentinel values for the mSyncInterval field of both Mailbox records
125    public static final int CHECK_INTERVAL_NEVER = -1;
126    public static final int CHECK_INTERVAL_PUSH = -2;
127    // The following two sentinel values are used by EAS
128    // Ping indicates that the EAS mailbox is synced based on a "ping" from the server
129    public static final int CHECK_INTERVAL_PING = -3;
130    // Push-Hold indicates an EAS push or ping Mailbox shouldn't sync just yet
131    public static final int CHECK_INTERVAL_PUSH_HOLD = -4;
132
133    // Sentinel for PARENT_KEY.  Use NO_MAILBOX for toplevel mailboxes (i.e. no parents).
134    public static final long PARENT_KEY_UNINITIALIZED = 0L;
135
136    private static final String WHERE_TYPE_AND_ACCOUNT_KEY =
137        MailboxColumns.TYPE + "=? and " + MailboxColumns.ACCOUNT_KEY + "=?";
138
139    public static final Integer[] INVALID_DROP_TARGETS = new Integer[] {Mailbox.TYPE_DRAFTS,
140        Mailbox.TYPE_OUTBOX, Mailbox.TYPE_SENT};
141
142    public static final String USER_VISIBLE_MAILBOX_SELECTION =
143        MailboxColumns.TYPE + "<" + Mailbox.TYPE_NOT_EMAIL +
144        " AND " + MailboxColumns.FLAG_VISIBLE + "=1";
145
146    // Types of mailboxes.  The list is ordered to match a typical UI presentation, e.g.
147    // placing the inbox at the top.
148    // Arrays of "special_mailbox_display_names" and "special_mailbox_icons" are depends on
149    // types Id of mailboxes.
150    /** No type specified */
151    public static final int TYPE_NONE = -1;
152    /** The "main" mailbox for the account, almost always referred to as "Inbox" */
153    public static final int TYPE_INBOX = 0;
154    // Types of mailboxes
155    /** Generic mailbox that holds mail */
156    public static final int TYPE_MAIL = 1;
157    /** Parent-only mailbox; does not hold any mail */
158    public static final int TYPE_PARENT = 2;
159    /** Drafts mailbox */
160    public static final int TYPE_DRAFTS = 3;
161    /** Local mailbox associated with the account's outgoing mail */
162    public static final int TYPE_OUTBOX = 4;
163    /** Sent mail; mail that was sent from the account */
164    public static final int TYPE_SENT = 5;
165    /** Deleted mail */
166    public static final int TYPE_TRASH = 6;
167    /** Junk mail */
168    public static final int TYPE_JUNK = 7;
169    /** Search results */
170    public static final int TYPE_SEARCH = 8;
171
172    // Types after this are used for non-mail mailboxes (as in EAS)
173    public static final int TYPE_NOT_EMAIL = 0x40;
174    public static final int TYPE_CALENDAR = 0x41;
175    public static final int TYPE_CONTACTS = 0x42;
176    public static final int TYPE_TASKS = 0x43;
177    public static final int TYPE_EAS_ACCOUNT_MAILBOX = 0x44;
178    public static final int TYPE_UNKNOWN = 0x45;
179
180    public static final int TYPE_NOT_SYNCABLE = 0x100;
181    // A mailbox that holds Messages that are attachments
182    public static final int TYPE_ATTACHMENT = 0x101;
183
184    // Bit field flags; each is defined below
185    // Warning: Do not read these flags until POP/IMAP/EAS all populate them
186    /** No flags set */
187    public static final int FLAG_NONE = 0;
188    /** Has children in the mailbox hierarchy */
189    public static final int FLAG_HAS_CHILDREN = 1<<0;
190    /** Children are visible in the UI */
191    public static final int FLAG_CHILDREN_VISIBLE = 1<<1;
192    /** cannot receive "pushed" mail */
193    public static final int FLAG_CANT_PUSH = 1<<2;
194    /** can hold emails (i.e. some parent mailboxes cannot themselves contain mail) */
195    public static final int FLAG_HOLDS_MAIL = 1<<3;
196    /** can be used as a target for moving messages within the account */
197    public static final int FLAG_ACCEPTS_MOVED_MAIL = 1<<4;
198    /** can be used as a target for appending messages */
199    public static final int FLAG_ACCEPTS_APPENDED_MAIL = 1<<5;
200
201    // Magic mailbox ID's
202    // NOTE:  This is a quick solution for merged mailboxes.  I would rather implement this
203    // with a more generic way of packaging and sharing queries between activities
204    public static final long QUERY_ALL_INBOXES = -2;
205    public static final long QUERY_ALL_UNREAD = -3;
206    public static final long QUERY_ALL_FAVORITES = -4;
207    public static final long QUERY_ALL_DRAFTS = -5;
208    public static final long QUERY_ALL_OUTBOX = -6;
209
210    public Mailbox() {
211        mBaseUri = CONTENT_URI;
212    }
213
214     /**
215     * Restore a Mailbox from the database, given its unique id
216     * @param context
217     * @param id
218     * @return the instantiated Mailbox
219     */
220    public static Mailbox restoreMailboxWithId(Context context, long id) {
221        return EmailContent.restoreContentWithId(context, Mailbox.class,
222                Mailbox.CONTENT_URI, Mailbox.CONTENT_PROJECTION, id);
223    }
224
225    /**
226     * Builds a new mailbox with "typical" settings for a system mailbox, such as a local "Drafts"
227     * mailbox. This is useful for protocols like POP3 or IMAP who don't have certain local
228     * system mailboxes synced with the server.
229     * Note: the mailbox is not persisted - clients must call {@link #save} themselves.
230     */
231    public static Mailbox newSystemMailbox(long accountId, int mailboxType, String name) {
232        if (mailboxType == Mailbox.TYPE_MAIL) {
233            throw new IllegalArgumentException("Cannot specify TYPE_MAIL for a system mailbox");
234        }
235        Mailbox box = new Mailbox();
236        box.mAccountKey = accountId;
237        box.mType = mailboxType;
238        box.mSyncInterval = Account.CHECK_INTERVAL_NEVER;
239        box.mFlagVisible = true;
240        box.mServerId = box.mDisplayName = name;
241        box.mParentKey = Mailbox.NO_MAILBOX;
242        box.mFlags = Mailbox.FLAG_HOLDS_MAIL;
243        return box;
244    }
245
246    /**
247     * Returns a Mailbox from the database, given its pathname and account id. All mailbox
248     * paths for a particular account must be unique. Paths are stored in the column
249     * {@link MailboxColumns#SERVER_ID} for want of yet another column in the table.
250     * @param context
251     * @param accountId the ID of the account
252     * @param path the fully qualified, remote pathname
253     */
254    public static Mailbox restoreMailboxForPath(Context context, long accountId, String path) {
255        Cursor c = context.getContentResolver().query(
256                Mailbox.CONTENT_URI,
257                Mailbox.CONTENT_PROJECTION,
258                Mailbox.PATH_AND_ACCOUNT_SELECTION,
259                new String[] { path, Long.toString(accountId) },
260                null);
261        if (c == null) throw new ProviderUnavailableException();
262        try {
263            Mailbox mailbox = null;
264            if (c.moveToFirst()) {
265                mailbox = getContent(c, Mailbox.class);
266                if (c.moveToNext()) {
267                    Log.w(Logging.LOG_TAG, "Multiple mailboxes named \"" + path + "\"");
268                }
269            } else {
270                Log.i(Logging.LOG_TAG, "Could not find mailbox at \"" + path + "\"");
271            }
272            return mailbox;
273        } finally {
274            c.close();
275        }
276    }
277
278    /**
279     * Returns a {@link Mailbox} for the given path. If the path is not in the database, a new
280     * mailbox will be created.
281     */
282    public static Mailbox getMailboxForPath(Context context, long accountId, String path) {
283        Mailbox mailbox = restoreMailboxForPath(context, accountId, path);
284        if (mailbox == null) {
285            mailbox = new Mailbox();
286        }
287        return mailbox;
288    }
289
290    @Override
291    public void restore(Cursor cursor) {
292        mBaseUri = CONTENT_URI;
293        mId = cursor.getLong(CONTENT_ID_COLUMN);
294        mDisplayName = cursor.getString(CONTENT_DISPLAY_NAME_COLUMN);
295        mServerId = cursor.getString(CONTENT_SERVER_ID_COLUMN);
296        mParentServerId = cursor.getString(CONTENT_PARENT_SERVER_ID_COLUMN);
297        mParentKey = cursor.getLong(CONTENT_PARENT_KEY_COLUMN);
298        mAccountKey = cursor.getLong(CONTENT_ACCOUNT_KEY_COLUMN);
299        mType = cursor.getInt(CONTENT_TYPE_COLUMN);
300        mDelimiter = cursor.getInt(CONTENT_DELIMITER_COLUMN);
301        mSyncKey = cursor.getString(CONTENT_SYNC_KEY_COLUMN);
302        mSyncLookback = cursor.getInt(CONTENT_SYNC_LOOKBACK_COLUMN);
303        mSyncInterval = cursor.getInt(CONTENT_SYNC_INTERVAL_COLUMN);
304        mSyncTime = cursor.getLong(CONTENT_SYNC_TIME_COLUMN);
305        mFlagVisible = cursor.getInt(CONTENT_FLAG_VISIBLE_COLUMN) == 1;
306        mFlags = cursor.getInt(CONTENT_FLAGS_COLUMN);
307        mVisibleLimit = cursor.getInt(CONTENT_VISIBLE_LIMIT_COLUMN);
308        mSyncStatus = cursor.getString(CONTENT_SYNC_STATUS_COLUMN);
309        mLastSeenMessageKey = cursor.getLong(CONTENT_LAST_SEEN_MESSAGE_KEY_COLUMN);
310        mLastTouchedTime = cursor.getLong(CONTENT_LAST_TOUCHED_TIME_COLUMN);
311    }
312
313    @Override
314    public ContentValues toContentValues() {
315        ContentValues values = new ContentValues();
316        values.put(MailboxColumns.DISPLAY_NAME, mDisplayName);
317        values.put(MailboxColumns.SERVER_ID, mServerId);
318        values.put(MailboxColumns.PARENT_SERVER_ID, mParentServerId);
319        values.put(MailboxColumns.PARENT_KEY, mParentKey);
320        values.put(MailboxColumns.ACCOUNT_KEY, mAccountKey);
321        values.put(MailboxColumns.TYPE, mType);
322        values.put(MailboxColumns.DELIMITER, mDelimiter);
323        values.put(MailboxColumns.SYNC_KEY, mSyncKey);
324        values.put(MailboxColumns.SYNC_LOOKBACK, mSyncLookback);
325        values.put(MailboxColumns.SYNC_INTERVAL, mSyncInterval);
326        values.put(MailboxColumns.SYNC_TIME, mSyncTime);
327        values.put(MailboxColumns.FLAG_VISIBLE, mFlagVisible);
328        values.put(MailboxColumns.FLAGS, mFlags);
329        values.put(MailboxColumns.VISIBLE_LIMIT, mVisibleLimit);
330        values.put(MailboxColumns.SYNC_STATUS, mSyncStatus);
331        values.put(MailboxColumns.LAST_SEEN_MESSAGE_KEY, mLastSeenMessageKey);
332        values.put(MailboxColumns.LAST_TOUCHED_TIME, mLastTouchedTime);
333        return values;
334    }
335
336    /**
337     * Convenience method to return the id of a given type of Mailbox for a given Account; the
338     * common Mailbox types (Inbox, Outbox, Sent, Drafts, Trash, and Search) are all cached by
339     * EmailProvider; therefore, we warn if the mailbox is not found in the cache
340     *
341     * @param context the caller's context, used to get a ContentResolver
342     * @param accountId the id of the account to be queried
343     * @param type the mailbox type, as defined above
344     * @return the id of the mailbox, or -1 if not found
345     */
346    public static long findMailboxOfType(Context context, long accountId, int type) {
347        // First use special URI
348        Uri uri = FROM_ACCOUNT_AND_TYPE_URI.buildUpon().appendPath(Long.toString(accountId))
349            .appendPath(Integer.toString(type)).build();
350        Cursor c = context.getContentResolver().query(uri, ID_PROJECTION, null, null, null);
351        if (c != null) {
352            try {
353                c.moveToFirst();
354                Long mailboxId = c.getLong(ID_PROJECTION_COLUMN);
355                if (mailboxId != null
356                        && mailboxId != 0L
357                        && mailboxId != NO_MAILBOX) {
358                    return mailboxId;
359                }
360            } finally {
361                c.close();
362            }
363        }
364        // Fallback to querying the database directly.
365        String[] bindArguments = new String[] {Long.toString(type), Long.toString(accountId)};
366        return Utility.getFirstRowLong(context, Mailbox.CONTENT_URI,
367                ID_PROJECTION, WHERE_TYPE_AND_ACCOUNT_KEY, bindArguments, null,
368                ID_PROJECTION_COLUMN, NO_MAILBOX);
369    }
370
371    /**
372     * Convenience method that returns the mailbox found using the method above
373     */
374    public static Mailbox restoreMailboxOfType(Context context, long accountId, int type) {
375        long mailboxId = findMailboxOfType(context, accountId, type);
376        if (mailboxId != Mailbox.NO_MAILBOX) {
377            return Mailbox.restoreMailboxWithId(context, mailboxId);
378        }
379        return null;
380    }
381
382    public static int getUnreadCountByAccountAndMailboxType(Context context, long accountId,
383            int type) {
384        return Utility.getFirstRowInt(context, Mailbox.CONTENT_URI,
385                MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION,
386                ACCOUNT_AND_MAILBOX_TYPE_SELECTION,
387                new String[] { String.valueOf(accountId), String.valueOf(type) },
388                null, UNREAD_COUNT_COUNT_COLUMN, 0);
389    }
390
391    public static int getUnreadCountByMailboxType(Context context, int type) {
392        return Utility.getFirstRowInt(context, Mailbox.CONTENT_URI,
393                MAILBOX_SUM_OF_UNREAD_COUNT_PROJECTION,
394                MAILBOX_TYPE_SELECTION,
395                new String[] { String.valueOf(type) }, null, UNREAD_COUNT_COUNT_COLUMN, 0);
396    }
397
398    public static int getMessageCountByMailboxType(Context context, int type) {
399        return Utility.getFirstRowInt(context, Mailbox.CONTENT_URI,
400                MAILBOX_SUM_OF_MESSAGE_COUNT_PROJECTION,
401                MAILBOX_TYPE_SELECTION,
402                new String[] { String.valueOf(type) }, null, MESSAGE_COUNT_COUNT_COLUMN, 0);
403    }
404
405    /**
406     * Return the mailbox for a message with a given id
407     * @param context the caller's context
408     * @param messageId the id of the message
409     * @return the mailbox, or null if the mailbox doesn't exist
410     */
411    public static Mailbox getMailboxForMessageId(Context context, long messageId) {
412        long mailboxId = Message.getKeyColumnLong(context, messageId,
413                MessageColumns.MAILBOX_KEY);
414        if (mailboxId != -1) {
415            return Mailbox.restoreMailboxWithId(context, mailboxId);
416        }
417        return null;
418    }
419
420    /**
421     * @return mailbox type, or -1 if mailbox not found.
422     */
423    public static int getMailboxType(Context context, long mailboxId) {
424        Uri url = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId);
425        return Utility.getFirstRowInt(context, url, MAILBOX_TYPE_PROJECTION,
426                null, null, null, MAILBOX_TYPE_TYPE_COLUMN, -1);
427    }
428
429    /**
430     * @return mailbox display name, or null if mailbox not found.
431     */
432    public static String getDisplayName(Context context, long mailboxId) {
433        Uri url = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mailboxId);
434        return Utility.getFirstRowString(context, url, MAILBOX_DISPLAY_NAME_PROJECTION,
435                null, null, null, MAILBOX_DISPLAY_NAME_COLUMN);
436    }
437
438    /**
439     * @param mailboxId ID of a mailbox.  This method accepts magic mailbox IDs, such as
440     * {@link #QUERY_ALL_INBOXES}. (They're all non-refreshable.)
441     * @return true if a mailbox is refreshable.
442     */
443    public static boolean isRefreshable(Context context, long mailboxId) {
444        if (mailboxId < 0) {
445            return false; // magic mailboxes
446        }
447        switch (getMailboxType(context, mailboxId)) {
448            case -1: // not found
449            case TYPE_DRAFTS:
450            case TYPE_OUTBOX:
451                return false;
452        }
453        return true;
454    }
455
456    /**
457     * @return whether or not this mailbox supports moving messages out of it
458     */
459    public boolean canHaveMessagesMoved() {
460        switch (mType) {
461            case TYPE_INBOX:
462            case TYPE_MAIL:
463            case TYPE_TRASH:
464            case TYPE_JUNK:
465                return true;
466        }
467        return false; // TYPE_DRAFTS, TYPE_OUTBOX, TYPE_SENT, etc
468    }
469
470    /**
471     * @return whether or not this mailbox retrieves its data from the server (as opposed to just
472     *     a local mailbox that is never synced).
473     */
474    public boolean loadsFromServer(String protocol) {
475        if (HostAuth.SCHEME_EAS.equals(protocol)) {
476            return mType != Mailbox.TYPE_DRAFTS
477                    && mType != Mailbox.TYPE_OUTBOX
478                    && mType != Mailbox.TYPE_SEARCH
479                    && mType < Mailbox.TYPE_NOT_SYNCABLE;
480
481        } else if (HostAuth.SCHEME_IMAP.equals(protocol)) {
482            // TODO: actually use a sync flag when creating the mailboxes. Right now we use an
483            // approximation for IMAP.
484            return mType != Mailbox.TYPE_DRAFTS
485                    && mType != Mailbox.TYPE_OUTBOX
486                    && mType != Mailbox.TYPE_SEARCH;
487
488        } else if (HostAuth.SCHEME_POP3.equals(protocol)) {
489            return TYPE_INBOX == mType;
490        }
491
492        return false;
493    }
494
495    /**
496     * @return true if messages in a mailbox of a type can be replied/forwarded.
497     */
498    public static boolean isMailboxTypeReplyAndForwardable(int type) {
499        return (type != TYPE_TRASH) && (type != TYPE_DRAFTS);
500    }
501
502    /**
503     * Returns a set of hashes that can identify this mailbox. These can be used to
504     * determine if any of the fields have been modified.
505     */
506    public Object[] getHashes() {
507        Object[] hash = new Object[CONTENT_PROJECTION.length];
508
509        hash[CONTENT_ID_COLUMN]
510             = mId;
511        hash[CONTENT_DISPLAY_NAME_COLUMN]
512                = mDisplayName;
513        hash[CONTENT_SERVER_ID_COLUMN]
514                = mServerId;
515        hash[CONTENT_PARENT_SERVER_ID_COLUMN]
516                = mParentServerId;
517        hash[CONTENT_ACCOUNT_KEY_COLUMN]
518                = mAccountKey;
519        hash[CONTENT_TYPE_COLUMN]
520                = mType;
521        hash[CONTENT_DELIMITER_COLUMN]
522                = mDelimiter;
523        hash[CONTENT_SYNC_KEY_COLUMN]
524                = mSyncKey;
525        hash[CONTENT_SYNC_LOOKBACK_COLUMN]
526                = mSyncLookback;
527        hash[CONTENT_SYNC_INTERVAL_COLUMN]
528                = mSyncInterval;
529        hash[CONTENT_SYNC_TIME_COLUMN]
530                = mSyncTime;
531        hash[CONTENT_FLAG_VISIBLE_COLUMN]
532                = mFlagVisible;
533        hash[CONTENT_FLAGS_COLUMN]
534                = mFlags;
535        hash[CONTENT_VISIBLE_LIMIT_COLUMN]
536                = mVisibleLimit;
537        hash[CONTENT_SYNC_STATUS_COLUMN]
538                = mSyncStatus;
539        hash[CONTENT_PARENT_KEY_COLUMN]
540                = mParentKey;
541        hash[CONTENT_LAST_SEEN_MESSAGE_KEY_COLUMN]
542                = mLastSeenMessageKey;
543        hash[CONTENT_LAST_TOUCHED_TIME_COLUMN]
544                = mLastTouchedTime;
545        return hash;
546    }
547
548    // Parcelable
549    @Override
550    public int describeContents() {
551        return 0;
552    }
553
554    // Parcelable
555    @Override
556    public void writeToParcel(Parcel dest, int flags) {
557        dest.writeParcelable(mBaseUri, flags);
558        dest.writeLong(mId);
559        dest.writeString(mDisplayName);
560        dest.writeString(mServerId);
561        dest.writeString(mParentServerId);
562        dest.writeLong(mParentKey);
563        dest.writeLong(mAccountKey);
564        dest.writeInt(mType);
565        dest.writeInt(mDelimiter);
566        dest.writeString(mSyncKey);
567        dest.writeInt(mSyncLookback);
568        dest.writeInt(mSyncInterval);
569        dest.writeLong(mSyncTime);
570        dest.writeInt(mFlagVisible ? 1 : 0);
571        dest.writeInt(mFlags);
572        dest.writeInt(mVisibleLimit);
573        dest.writeString(mSyncStatus);
574        dest.writeLong(mLastSeenMessageKey);
575        dest.writeLong(mLastTouchedTime);
576    }
577
578    public Mailbox(Parcel in) {
579        mBaseUri = in.readParcelable(null);
580        mId = in.readLong();
581        mDisplayName = in.readString();
582        mServerId = in.readString();
583        mParentServerId = in.readString();
584        mParentKey = in.readLong();
585        mAccountKey = in.readLong();
586        mType = in.readInt();
587        mDelimiter = in.readInt();
588        mSyncKey = in.readString();
589        mSyncLookback = in.readInt();
590        mSyncInterval = in.readInt();
591        mSyncTime = in.readLong();
592        mFlagVisible = in.readInt() == 1;
593        mFlags = in.readInt();
594        mVisibleLimit = in.readInt();
595        mSyncStatus = in.readString();
596        mLastSeenMessageKey = in.readLong();
597        mLastTouchedTime = in.readLong();
598    }
599
600    public static final Parcelable.Creator<Mailbox> CREATOR = new Parcelable.Creator<Mailbox>() {
601        @Override
602        public Mailbox createFromParcel(Parcel source) {
603            return new Mailbox(source);
604        }
605
606        @Override
607        public Mailbox[] newArray(int size) {
608            return new Mailbox[size];
609        }
610    };
611}
612