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 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