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