Home | History | Annotate | Download | only in provider
      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 
     18 package com.android.emailcommon.provider;
     19 
     20 import android.content.ContentUris;
     21 import android.content.ContentValues;
     22 import android.content.Context;
     23 import android.database.Cursor;
     24 import android.net.Uri;
     25 import android.os.Parcel;
     26 import android.os.Parcelable;
     27 import android.util.Log;
     28 
     29 import com.android.emailcommon.Logging;
     30 import com.android.emailcommon.provider.EmailContent.MailboxColumns;
     31 import com.android.emailcommon.provider.EmailContent.SyncColumns;
     32 import com.android.emailcommon.utility.Utility;
     33 
     34 public 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