| package com.android.exchange.eas; |
| |
| import android.content.Context; |
| import android.database.Cursor; |
| |
| import com.android.emailcommon.TrafficFlags; |
| import com.android.emailcommon.provider.Account; |
| import com.android.emailcommon.provider.EmailContent.Message; |
| import com.android.emailcommon.provider.EmailContent.MessageColumns; |
| import com.android.emailcommon.provider.EmailContent.SyncColumns; |
| import com.android.emailcommon.provider.Mailbox; |
| import com.android.emailcommon.service.SyncWindow; |
| import com.android.exchange.Eas; |
| import com.android.exchange.adapter.AbstractSyncParser; |
| import com.android.exchange.adapter.EmailSyncParser; |
| import com.android.exchange.adapter.Serializer; |
| import com.android.exchange.adapter.Tags; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| |
| /** |
| * Subclass to handle sync details for mail collections. |
| */ |
| public class EasSyncMail extends EasSyncCollectionTypeBase { |
| |
| /** |
| * The projection used for building the fetch request list. |
| */ |
| private static final String[] FETCH_REQUEST_PROJECTION = { SyncColumns.SERVER_ID }; |
| private static final int FETCH_REQUEST_SERVER_ID = 0; |
| |
| private static final int EMAIL_WINDOW_SIZE = 10; |
| |
| |
| @Override |
| public int getTrafficFlag() { |
| return TrafficFlags.DATA_EMAIL; |
| } |
| |
| @Override |
| public void setSyncOptions(final Context context, final Serializer s, |
| final double protocolVersion, final Account account, final Mailbox mailbox, |
| final boolean isInitialSync, final int numWindows) throws IOException { |
| if (isInitialSync) { |
| // No special options to set for initial mailbox sync. |
| return; |
| } |
| |
| // Check for messages that aren't fully loaded. |
| final ArrayList<String> messagesToFetch = addToFetchRequestList(context, mailbox); |
| // The "empty" case is typical; we send a request for changes, and also specify a sync |
| // window, body preference type (HTML for EAS 12.0 and later; MIME for EAS 2.5), and |
| // truncation |
| // If there are fetch requests, we only want the fetches (i.e. no changes from the server) |
| // so we turn MIME support off. Note that we are always using EAS 2.5 if there are fetch |
| // requests |
| if (messagesToFetch.isEmpty()) { |
| // Permanently delete if in trash mailbox |
| // In Exchange 2003, deletes-as-moves tag = true; no tag = false |
| // In Exchange 2007 and up, deletes-as-moves tag is "0" (false) or "1" (true) |
| final boolean isTrashMailbox = mailbox.mType == Mailbox.TYPE_TRASH; |
| if (protocolVersion < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { |
| if (!isTrashMailbox) { |
| s.tag(Tags.SYNC_DELETES_AS_MOVES); |
| } |
| } else { |
| s.data(Tags.SYNC_DELETES_AS_MOVES, isTrashMailbox ? "0" : "1"); |
| } |
| s.tag(Tags.SYNC_GET_CHANGES); |
| |
| final int windowSize = numWindows * EMAIL_WINDOW_SIZE; |
| if (windowSize > MAX_WINDOW_SIZE + EMAIL_WINDOW_SIZE) { |
| throw new IOException("Max window size reached and still no data"); |
| } |
| s.data(Tags.SYNC_WINDOW_SIZE, |
| String.valueOf(windowSize < MAX_WINDOW_SIZE ? windowSize : MAX_WINDOW_SIZE)); |
| s.start(Tags.SYNC_OPTIONS); |
| // Set the lookback appropriately (EAS calls this a "filter") |
| s.data(Tags.SYNC_FILTER_TYPE, getEmailFilter(account, mailbox)); |
| // Set the truncation amount for all classes |
| if (protocolVersion >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { |
| s.start(Tags.BASE_BODY_PREFERENCE); |
| // HTML for email |
| s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML); |
| s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE); |
| s.end(); |
| } else { |
| // Use MIME data for EAS 2.5 |
| s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_MIME); |
| s.data(Tags.SYNC_MIME_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); |
| } |
| s.end(); |
| } else { |
| // If we have any messages that are not fully loaded, ask for plain text rather than |
| // MIME, to guarantee we'll get usable text body. This also means we should NOT ask for |
| // new messages -- we only want data for the message explicitly fetched. |
| s.start(Tags.SYNC_OPTIONS); |
| s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_TEXT); |
| s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); |
| s.end(); |
| |
| // Add FETCH commands for messages that need a body (i.e. we didn't find it during our |
| // earlier sync; this happens only in EAS 2.5 where the body couldn't be found after |
| // parsing the message's MIME data). |
| s.start(Tags.SYNC_COMMANDS); |
| for (final String serverId : messagesToFetch) { |
| s.start(Tags.SYNC_FETCH).data(Tags.SYNC_SERVER_ID, serverId).end(); |
| } |
| s.end(); |
| } |
| } |
| |
| @Override |
| public AbstractSyncParser getParser(final Context context, final Account account, |
| final Mailbox mailbox, final InputStream is) throws IOException { |
| return new EmailSyncParser(context, is, mailbox, account); |
| } |
| |
| /** |
| * Query the provider for partially loaded messages. |
| * @return Server ids for partially loaded messages. |
| */ |
| private ArrayList<String> addToFetchRequestList(final Context context, final Mailbox mailbox) { |
| final ArrayList<String> messagesToFetch = new ArrayList<String>(); |
| final Cursor c = context.getContentResolver().query(Message.CONTENT_URI, |
| FETCH_REQUEST_PROJECTION, MessageColumns.FLAG_LOADED + "=" + |
| Message.FLAG_LOADED_PARTIAL + " AND " + MessageColumns.MAILBOX_KEY + "=?", |
| new String[] {Long.toString(mailbox.mId)}, null); |
| if (c != null) { |
| try { |
| while (c.moveToNext()) { |
| messagesToFetch.add(c.getString(FETCH_REQUEST_SERVER_ID)); |
| } |
| } finally { |
| c.close(); |
| } |
| } |
| return messagesToFetch; |
| } |
| |
| /** |
| * Get the sync window for this collection and translate it to EAS's value for that (EAS refers |
| * to this as the "filter"). |
| * @param account The {@link Account} for this sync; its sync window is used if the mailbox |
| * doesn't specify an override. |
| * @param mailbox The {@link Mailbox} for this sync. |
| * @return The EAS string value for the sync window specified for this mailbox. |
| */ |
| private String getEmailFilter(final Account account, final Mailbox mailbox) { |
| final int syncLookback = mailbox.mSyncLookback == SyncWindow.SYNC_WINDOW_ACCOUNT |
| ? account.mSyncLookback : mailbox.mSyncLookback; |
| switch (syncLookback) { |
| case SyncWindow.SYNC_WINDOW_1_DAY: |
| return Eas.FILTER_1_DAY; |
| case SyncWindow.SYNC_WINDOW_3_DAYS: |
| return Eas.FILTER_3_DAYS; |
| case SyncWindow.SYNC_WINDOW_1_WEEK: |
| return Eas.FILTER_1_WEEK; |
| case SyncWindow.SYNC_WINDOW_2_WEEKS: |
| return Eas.FILTER_2_WEEKS; |
| case SyncWindow.SYNC_WINDOW_1_MONTH: |
| return Eas.FILTER_1_MONTH; |
| case SyncWindow.SYNC_WINDOW_ALL: |
| return Eas.FILTER_ALL; |
| default: |
| // Auto window is deprecated and will also use the default. |
| return Eas.FILTER_1_WEEK; |
| } |
| } |
| } |