Home | History | Annotate | Download | only in eas
      1 package com.android.exchange.eas;
      2 
      3 import android.content.Context;
      4 import android.database.Cursor;
      5 
      6 import com.android.emailcommon.TrafficFlags;
      7 import com.android.emailcommon.provider.Account;
      8 import com.android.emailcommon.provider.EmailContent.Message;
      9 import com.android.emailcommon.provider.EmailContent.MessageColumns;
     10 import com.android.emailcommon.provider.EmailContent.SyncColumns;
     11 import com.android.emailcommon.provider.Mailbox;
     12 import com.android.emailcommon.service.SyncWindow;
     13 import com.android.exchange.Eas;
     14 import com.android.exchange.adapter.AbstractSyncParser;
     15 import com.android.exchange.adapter.EmailSyncParser;
     16 import com.android.exchange.adapter.Serializer;
     17 import com.android.exchange.adapter.Tags;
     18 
     19 import java.io.IOException;
     20 import java.io.InputStream;
     21 import java.util.ArrayList;
     22 
     23 /**
     24  * Subclass to handle sync details for mail collections.
     25  */
     26 public class EasSyncMail extends EasSyncCollectionTypeBase {
     27 
     28     /**
     29      * The projection used for building the fetch request list.
     30      */
     31     private static final String[] FETCH_REQUEST_PROJECTION = { SyncColumns.SERVER_ID };
     32     private static final int FETCH_REQUEST_SERVER_ID = 0;
     33 
     34     private static final int EMAIL_WINDOW_SIZE = 10;
     35 
     36 
     37     @Override
     38     public int getTrafficFlag() {
     39         return TrafficFlags.DATA_EMAIL;
     40     }
     41 
     42     @Override
     43     public void setSyncOptions(final Context context, final Serializer s,
     44             final double protocolVersion, final Account account, final Mailbox mailbox,
     45             final boolean isInitialSync, final int numWindows) throws IOException {
     46         if (isInitialSync) {
     47             // No special options to set for initial mailbox sync.
     48             return;
     49         }
     50 
     51         // Check for messages that aren't fully loaded.
     52         final ArrayList<String> messagesToFetch = addToFetchRequestList(context, mailbox);
     53         // The "empty" case is typical; we send a request for changes, and also specify a sync
     54         // window, body preference type (HTML for EAS 12.0 and later; MIME for EAS 2.5), and
     55         // truncation
     56         // If there are fetch requests, we only want the fetches (i.e. no changes from the server)
     57         // so we turn MIME support off.  Note that we are always using EAS 2.5 if there are fetch
     58         // requests
     59         if (messagesToFetch.isEmpty()) {
     60             // Permanently delete if in trash mailbox
     61             // In Exchange 2003, deletes-as-moves tag = true; no tag = false
     62             // In Exchange 2007 and up, deletes-as-moves tag is "0" (false) or "1" (true)
     63             final boolean isTrashMailbox = mailbox.mType == Mailbox.TYPE_TRASH;
     64             if (protocolVersion < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
     65                 if (!isTrashMailbox) {
     66                     s.tag(Tags.SYNC_DELETES_AS_MOVES);
     67                 }
     68             } else {
     69                 s.data(Tags.SYNC_DELETES_AS_MOVES, isTrashMailbox ? "0" : "1");
     70             }
     71             s.tag(Tags.SYNC_GET_CHANGES);
     72 
     73             final int windowSize = numWindows * EMAIL_WINDOW_SIZE;
     74             if (windowSize > MAX_WINDOW_SIZE  + EMAIL_WINDOW_SIZE) {
     75                 throw new IOException("Max window size reached and still no data");
     76             }
     77             s.data(Tags.SYNC_WINDOW_SIZE,
     78                     String.valueOf(windowSize < MAX_WINDOW_SIZE ? windowSize : MAX_WINDOW_SIZE));
     79             s.start(Tags.SYNC_OPTIONS);
     80             // Set the lookback appropriately (EAS calls this a "filter")
     81             s.data(Tags.SYNC_FILTER_TYPE, getEmailFilter(account, mailbox));
     82             // Set the truncation amount for all classes
     83             if (protocolVersion >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) {
     84                 s.start(Tags.BASE_BODY_PREFERENCE);
     85                 // HTML for email
     86                 s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML);
     87                 s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE);
     88                 s.end();
     89             } else {
     90                 // Use MIME data for EAS 2.5
     91                 s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_MIME);
     92                 s.data(Tags.SYNC_MIME_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE);
     93             }
     94             s.end();
     95         } else {
     96             // If we have any messages that are not fully loaded, ask for plain text rather than
     97             // MIME, to guarantee we'll get usable text body. This also means we should NOT ask for
     98             // new messages -- we only want data for the message explicitly fetched.
     99             s.start(Tags.SYNC_OPTIONS);
    100             s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_TEXT);
    101             s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE);
    102             s.end();
    103 
    104             // Add FETCH commands for messages that need a body (i.e. we didn't find it during our
    105             // earlier sync; this happens only in EAS 2.5 where the body couldn't be found after
    106             // parsing the message's MIME data).
    107             s.start(Tags.SYNC_COMMANDS);
    108             for (final String serverId : messagesToFetch) {
    109                 s.start(Tags.SYNC_FETCH).data(Tags.SYNC_SERVER_ID, serverId).end();
    110             }
    111             s.end();
    112         }
    113     }
    114 
    115     @Override
    116     public AbstractSyncParser getParser(final Context context, final Account account,
    117             final Mailbox mailbox, final InputStream is) throws IOException {
    118         return new EmailSyncParser(context, is, mailbox, account);
    119     }
    120 
    121     /**
    122      * Query the provider for partially loaded messages.
    123      * @return Server ids for partially loaded messages.
    124      */
    125     private ArrayList<String> addToFetchRequestList(final Context context, final Mailbox mailbox) {
    126         final ArrayList<String> messagesToFetch = new ArrayList<String>();
    127         final Cursor c = context.getContentResolver().query(Message.CONTENT_URI,
    128                 FETCH_REQUEST_PROJECTION,  MessageColumns.FLAG_LOADED + "=" +
    129                 Message.FLAG_LOADED_PARTIAL + " AND " +  MessageColumns.MAILBOX_KEY + "=?",
    130                 new String[] {Long.toString(mailbox.mId)}, null);
    131         if (c != null) {
    132             try {
    133                 while (c.moveToNext()) {
    134                     messagesToFetch.add(c.getString(FETCH_REQUEST_SERVER_ID));
    135                 }
    136             } finally {
    137                 c.close();
    138             }
    139         }
    140         return messagesToFetch;
    141     }
    142 
    143     /**
    144      * Get the sync window for this collection and translate it to EAS's value for that (EAS refers
    145      * to this as the "filter").
    146      * @param account The {@link Account} for this sync; its sync window is used if the mailbox
    147      *                doesn't specify an override.
    148      * @param mailbox The {@link Mailbox} for this sync.
    149      * @return The EAS string value for the sync window specified for this mailbox.
    150      */
    151     private String getEmailFilter(final Account account, final Mailbox mailbox) {
    152         final int syncLookback = mailbox.mSyncLookback == SyncWindow.SYNC_WINDOW_ACCOUNT
    153                 ? account.mSyncLookback : mailbox.mSyncLookback;
    154         switch (syncLookback) {
    155             case SyncWindow.SYNC_WINDOW_1_DAY:
    156                 return Eas.FILTER_1_DAY;
    157             case SyncWindow.SYNC_WINDOW_3_DAYS:
    158                 return Eas.FILTER_3_DAYS;
    159             case SyncWindow.SYNC_WINDOW_1_WEEK:
    160                 return Eas.FILTER_1_WEEK;
    161             case SyncWindow.SYNC_WINDOW_2_WEEKS:
    162                 return Eas.FILTER_2_WEEKS;
    163             case SyncWindow.SYNC_WINDOW_1_MONTH:
    164                 return Eas.FILTER_1_MONTH;
    165             case SyncWindow.SYNC_WINDOW_ALL:
    166                 return Eas.FILTER_ALL;
    167             default:
    168                 // Auto window is deprecated and will also use the default.
    169                 return Eas.FILTER_1_WEEK;
    170         }
    171     }
    172 }
    173