EmailSyncAdapter.java revision b43c40606146babc767475bbabac5820efd4c604
1ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/* 2ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Copyright (C) 2008-2009 Marc Blank 3ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed to The Android Open Source Project. 4ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 5ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 6ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * you may not use this file except in compliance with the License. 7ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * You may obtain a copy of the License at 8ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 9ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 10ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 11ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Unless required by applicable law or agreed to in writing, software 12ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 13ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * See the License for the specific language governing permissions and 15ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * limitations under the License. 16ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 17ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 18ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpackage com.android.exchange.adapter; 19ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 209b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentProviderOperation; 219b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentResolver; 229b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentUris; 239b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentValues; 249b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.OperationApplicationException; 259b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.database.Cursor; 269b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.os.RemoteException; 279b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.util.Log; 289b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.webkit.MimeTypeMap; 299b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank 30c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.internet.MimeMessage; 31c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.internet.MimeUtility; 32c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.Address; 33c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.MeetingInfo; 34c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.MessagingException; 35c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.PackedString; 36c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.Part; 37c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent; 38c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Attachment; 39c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Body; 400565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport com.android.emailcommon.provider.EmailContent.MailboxColumns; 41c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Message; 42c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.MessageColumns; 43c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.SyncColumns; 444d8774462ace9a45154b2df418b9f2fe7a9c685dBen Komaloimport com.android.emailcommon.provider.Mailbox; 45d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blankimport com.android.emailcommon.provider.Policy; 46e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.service.SyncWindow; 47e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.utility.AttachmentUtilities; 48e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.utility.ConversionUtilities; 49c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.utility.Utility; 5077186bb1a174432ef272584374942d8b9228e39cMarc Blankimport com.android.exchange.CommandStatusException; 5100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.Eas; 52498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport com.android.exchange.EasResponse; 5300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.EasSyncService; 544471a6960d352242cc65bddf7888cc5335840c74Marc Blankimport com.android.exchange.MessageMoveRequest; 550565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport com.android.exchange.R; 56c10a3beef4f048292e6a4ceb31527c5123801517Marc Blankimport com.android.exchange.utility.CalendarUtilities; 57277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blankimport com.google.common.annotations.VisibleForTesting; 58ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 590565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport org.apache.http.HttpStatus; 600565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport org.apache.http.entity.ByteArrayEntity; 610565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 624f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport java.io.ByteArrayInputStream; 6300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException; 6400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream; 6500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList; 6636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blankimport java.util.Calendar; 6700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.GregorianCalendar; 6800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.TimeZone; 69ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 70ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/** 71ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Sync adapter for EAS email 72ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 73ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankpublic class EmailSyncAdapter extends AbstractSyncAdapter { 7500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 7691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_READ_COLUMN = 0; 7791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_MAILBOX_KEY_COLUMN = 1; 7891e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_SERVER_ID_COLUMN = 2; 7936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private static final int UPDATES_FLAG_COLUMN = 3; 8091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final String[] UPDATES_PROJECTION = 8136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank {MessageColumns.FLAG_READ, MessageColumns.MAILBOX_KEY, SyncColumns.SERVER_ID, 8236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank MessageColumns.FLAG_FAVORITE}; 8349c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank 8449c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_ID_COLUMN = 0; 8549c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_SUBJECT_COLUMN = 1; 8618e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank private static final String[] MESSAGE_ID_SUBJECT_PROJECTION = 8718e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank new String[] { Message.RECORD_ID, MessageColumns.SUBJECT }; 8818e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank 89c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private static final String WHERE_BODY_SOURCE_MESSAGE_KEY = Body.SOURCE_MESSAGE_KEY + "=?"; 90a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank private static final String WHERE_MAILBOX_KEY_AND_MOVED = 91a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank MessageColumns.MAILBOX_KEY + "=? AND (" + MessageColumns.FLAGS + "&" + 92a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank EasSyncService.MESSAGE_FLAG_MOVED_MESSAGE + ")!=0"; 934f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String[] FETCH_REQUEST_PROJECTION = 944f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {EmailContent.RECORD_ID, SyncColumns.SERVER_ID}; 954f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_RECORD_ID = 0; 964f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_SERVER_ID = 1; 974f15001bdfd11c79524b4e44d60041967779e763Marc Blank 984f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String EMAIL_WINDOW_SIZE = "5"; 9991e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 1009b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1019b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_REPLY = 1; 1029b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1039b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_REPLY_ALL = 2; 1049b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1059b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_FORWARD = 3; 106422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank 107498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final String[] mBindArguments = new String[2]; 108498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final String[] mBindArgument = new String[1]; 109ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 110936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 111936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy ArrayList<Long> mDeletedIdList = new ArrayList<Long>(); 112936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 113936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy ArrayList<Long> mUpdatedIdList = new ArrayList<Long>(); 114498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<FetchRequest> mFetchRequestList = new ArrayList<FetchRequest>(); 1154f15001bdfd11c79524b4e44d60041967779e763Marc Blank private boolean mFetchNeeded = false; 116ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1178efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Holds the parser's value for isLooping() 118936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private boolean mIsLooping = false; 1198efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 120d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank // The policy (if any) for this adapter's Account 121d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank private final Policy mPolicy; 122d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank 1236f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank public EmailSyncAdapter(EasSyncService service) { 1246f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank super(service); 125d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank // If we've got an account with a policy, cache it now 126d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank if (mAccount.mPolicyKey != 0) { 127d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 128d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } else { 129d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank mPolicy = null; 130d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 1316f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank } 1326f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank 1336f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank @Override 1346f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank public void wipe() { 1356f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.CONTENT_URI, 1366f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 1376f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.DELETED_CONTENT_URI, 1386f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 1396f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.UPDATED_CONTENT_URI, 1406f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 141057faf66b737bbc7df2eaf77ee7a63827785e1b9Marc Blank mService.clearRequests(); 142a261805b03b853cce662b679da3e16120d521b7eMarc Blank mFetchRequestList.clear(); 143134346f5b886e6b53074238546653cdc76bbe868Marc Blank // Delete attachments... 144c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext, mAccount.mId, mMailbox.mId); 145ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 146ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1474f15001bdfd11c79524b4e44d60041967779e763Marc Blank private String getEmailFilter() { 1488bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank int syncLookback = mMailbox.mSyncLookback; 149b43c40606146babc767475bbabac5820efd4c604Marc Blank if (syncLookback == SyncWindow.SYNC_WINDOW_UNKNOWN 150b43c40606146babc767475bbabac5820efd4c604Marc Blank || mMailbox.mType == Mailbox.TYPE_INBOX) { 1518bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank syncLookback = mAccount.mSyncLookback; 1528bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank } 1538bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank switch (syncLookback) { 1540565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank case SyncWindow.SYNC_WINDOW_AUTO: 1550565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return Eas.FILTER_AUTO; 156e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_DAY: 1574f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_DAY; 158e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_3_DAYS: 1594f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_3_DAYS; 160e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_WEEK: 1614f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 162e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_2_WEEKS: 1634f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_2_WEEKS; 164e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_MONTH: 1654f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_MONTH; 166e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_ALL: 1674f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_ALL; 1684f15001bdfd11c79524b4e44d60041967779e763Marc Blank default: 1694f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 1704f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1714f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1724f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1734f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 1744f15001bdfd11c79524b4e44d60041967779e763Marc Blank * Holder for fetch request information (record id and server id) 1754f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 176936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private static class FetchRequest { 177936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @SuppressWarnings("unused") 1784f15001bdfd11c79524b4e44d60041967779e763Marc Blank final long messageId; 1794f15001bdfd11c79524b4e44d60041967779e763Marc Blank final String serverId; 1804f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1814f15001bdfd11c79524b4e44d60041967779e763Marc Blank FetchRequest(long _messageId, String _serverId) { 1824f15001bdfd11c79524b4e44d60041967779e763Marc Blank messageId = _messageId; 1834f15001bdfd11c79524b4e44d60041967779e763Marc Blank serverId = _serverId; 1844f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1854f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1864f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1874f15001bdfd11c79524b4e44d60041967779e763Marc Blank @Override 1884f15001bdfd11c79524b4e44d60041967779e763Marc Blank public void sendSyncOptions(Double protocolVersion, Serializer s) 1894f15001bdfd11c79524b4e44d60041967779e763Marc Blank throws IOException { 1904f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.clear(); 1914f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find partially loaded messages; this should typically be a rare occurrence 1924f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = mContext.getContentResolver().query(Message.CONTENT_URI, 1934f15001bdfd11c79524b4e44d60041967779e763Marc Blank FETCH_REQUEST_PROJECTION, 1944f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.FLAG_LOADED + "=" + Message.FLAG_LOADED_PARTIAL + " AND " + 1954f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.MAILBOX_KEY + "=?", 1964f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {Long.toString(mMailbox.mId)}, null); 1974f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 1984f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Put all of these messages into a list; we'll need both id and server id 1994f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (c.moveToNext()) { 2004f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.add(new FetchRequest(c.getLong(FETCH_REQUEST_RECORD_ID), 2014f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.getString(FETCH_REQUEST_SERVER_ID))); 2024f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2034f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 2044f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 2054f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2064f15001bdfd11c79524b4e44d60041967779e763Marc Blank 2074f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The "empty" case is typical; we send a request for changes, and also specify a sync 2084f15001bdfd11c79524b4e44d60041967779e763Marc Blank // window, body preference type (HTML for EAS 12.0 and later; MIME for EAS 2.5), and 2094f15001bdfd11c79524b4e44d60041967779e763Marc Blank // truncation 2104f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If there are fetch requests, we only want the fetches (i.e. no changes from the server) 2114f15001bdfd11c79524b4e44d60041967779e763Marc Blank // so we turn MIME support off. Note that we are always using EAS 2.5 if there are fetch 2124f15001bdfd11c79524b4e44d60041967779e763Marc Blank // requests 2134f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchRequestList.isEmpty()) { 2144f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.tag(Tags.SYNC_DELETES_AS_MOVES); 2154f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.tag(Tags.SYNC_GET_CHANGES); 2164f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_WINDOW_SIZE, EMAIL_WINDOW_SIZE); 2174f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 2184f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the lookback appropriately (EAS calls this a "filter") 2190d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank String filter = getEmailFilter(); 2200d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank // We shouldn't get FILTER_AUTO here, but if we do, make it something legal... 2210d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank if (filter.equals(Eas.FILTER_AUTO)) { 2220d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank filter = Eas.FILTER_3_DAYS; 2230d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank } 224aead58d49204e28a78523c19bd86ad14a0599318Marc Blank s.data(Tags.SYNC_FILTER_TYPE, filter); 2254f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the truncation amount for all classes 2264f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (protocolVersion >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 2274f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.BASE_BODY_PREFERENCE); 2284f15001bdfd11c79524b4e44d60041967779e763Marc Blank // HTML for email 2294f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML); 2304f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE); 2314f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2324f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 2334f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Use MIME data for EAS 2.5 2344f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_MIME); 2354f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 2364f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2374f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2384f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 2394f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 2404f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Ask for plain text, rather than MIME data. This guarantees that we'll get a usable 2414f15001bdfd11c79524b4e44d60041967779e763Marc Blank // text body 2424f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_TEXT); 2434f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 2444f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2454f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2464f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2474f15001bdfd11c79524b4e44d60041967779e763Marc Blank 248ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 24977186bb1a174432ef272584374942d8b9228e39cMarc Blank public boolean parse(InputStream is) throws IOException, CommandStatusException { 25048af7392c82262d17700e3fbdccf3a582809d449Marc Blank EasEmailSyncParser p = new EasEmailSyncParser(is, this); 2514f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = false; 2528efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank boolean res = p.parse(); 2538efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Hold on to the parser's value for isLooping() to pass back to the service 2548efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank mIsLooping = p.isLooping(); 2554f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we've need a body fetch, or we've just finished one, return true in order to continue 2564f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchNeeded || !mFetchRequestList.isEmpty()) { 2574f15001bdfd11c79524b4e44d60041967779e763Marc Blank return true; 2584f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2590565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 2600565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Don't check for "auto" on the initial sync 2610565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (!("0".equals(mMailbox.mSyncKey))) { 2620565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // We've completed the first successful sync 2630565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (getEmailFilter().equals(Eas.FILTER_AUTO)) { 2640565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank getAutomaticLookback(); 2650565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 2660565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 2670565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 2688efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return res; 2698efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank } 2708efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 2710565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private void getAutomaticLookback() throws IOException { 2720565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If we're using an auto lookback, check how many items in the past week 2730565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // TODO Make the literal ints below constants once we twiddle them a bit 2740565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank int items = getEstimate(Eas.FILTER_1_WEEK); 2750565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank int lookback; 2760565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (items > 1050) { 2770565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Over 150/day, just use one day (smallest) 2780565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_DAY; 2790565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 350 || (items == -1)) { 2800565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 50-150/day, use 3 days (150 to 450 messages synced) 2810565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_3_DAYS; 2820565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 150) { 2830565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 20-50/day, use 1 week (140 to 350 messages synced) 2840565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_WEEK; 2850565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 75) { 2860565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 10-25/day, use 1 week (140 to 350 messages synced) 2870565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_2_WEEKS; 2880565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items < 5) { 2890565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If there are only a couple, see if it makes sense to get everything 2900565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank items = getEstimate(Eas.FILTER_ALL); 2910565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (items >= 0 && items < 100) { 2920565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_ALL; 2930565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 2940565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_MONTH; 2950565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 2960565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 2970565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_MONTH; 2980565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 2990565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Store the new lookback and persist it 3000565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank mMailbox.mSyncLookback = lookback; 3010565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank ContentValues cv = new ContentValues(); 3020565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank cv.put(MailboxColumns.SYNC_LOOKBACK, lookback); 3030565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank mContentResolver.update(ContentUris.withAppendedId( 3040565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Mailbox.CONTENT_URI, mMailbox.mId), cv, null, null); 3050565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // STOPSHIP Temporary UI - Let the user know 3060565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank CharSequence[] windowEntries = mContext.getResources().getTextArray( 3070565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank R.array.account_settings_mail_window_entries); 3080565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Utility.showToast(mContext, "Auto lookback: " + windowEntries[lookback]); 3090565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3100565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 311936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private static class GetItemEstimateParser extends Parser { 3120565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private static final String TAG = "GetItemEstimateParser"; 3130565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private int mEstimate = -1; 3140565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3150565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public GetItemEstimateParser(InputStream in) throws IOException { 3160565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank super(in); 3170565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3180565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 319936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @Override 3200565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public boolean parse() throws IOException { 3210565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Loop here through the remaining xml 3220565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(START_DOCUMENT) != END_DOCUMENT) { 3230565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_GET_ITEM_ESTIMATE) { 3240565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseGetItemEstimate(); 3250565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3260565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3270565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3280565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3290565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return true; 3300565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3310565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3320565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseGetItemEstimate() throws IOException { 3330565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_GET_ITEM_ESTIMATE) != END) { 3340565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_RESPONSE) { 3350565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseResponse(); 3360565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3370565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3380565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3390565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3400565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3410565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3420565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseResponse() throws IOException { 3430565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_RESPONSE) != END) { 3440565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_STATUS) { 3450565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Log.d(TAG, "GIE status: " + getValue()); 3460565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_COLLECTION) { 3470565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseCollection(); 3480565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3490565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3500565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3510565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3520565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3530565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3540565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseCollection() throws IOException { 3550565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_COLLECTION) != END) { 3560565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_CLASS) { 3570565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Log.d(TAG, "GIE class: " + getValue()); 3580565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_COLLECTION_ID) { 3590565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Log.d(TAG, "GIE collectionId: " + getValue()); 3600565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_ESTIMATE) { 3610565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank mEstimate = getValueInt(); 3620565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Log.d(TAG, "GIE estimate: " + mEstimate); 3630565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3640565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3650565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3660565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3670565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3680565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3690565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3700565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank /** 3710565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * Return the estimated number of items to be synced in the current mailbox, based on the 3720565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * passed in filter argument 3730565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @param filter an EAS "window" filter 3740565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @return the estimated number of items to be synced, or -1 if unknown 3750565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @throws IOException 3760565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank */ 3770565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private int getEstimate(String filter) throws IOException { 3780565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Serializer s = new Serializer(); 379277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex10 = mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE; 380277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex03 = mService.mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE; 381277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex07 = !ex10 && !ex03; 3820565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3830565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank String className = getCollectionName(); 3840565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank String syncKey = getSyncKey(); 3850565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank userLog("gie, sending ", className, " syncKey: ", syncKey); 386277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank 3870565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.start(Tags.GIE_GET_ITEM_ESTIMATE).start(Tags.GIE_COLLECTIONS); 3880565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.start(Tags.GIE_COLLECTION); 389277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank if (ex07) { 390277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2007 likes collection id first 391277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 392277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_FILTER_TYPE, filter); 393277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 394277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank } else if (ex03) { 395277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2003 needs the "class" element 3960565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.data(Tags.GIE_CLASS, className); 397277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 398277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 399277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_FILTER_TYPE, filter); 400277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank } else { 401277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2010 requires the filter inside an OPTIONS container and sync key first 402277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 403277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 404277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.start(Tags.SYNC_OPTIONS).data(Tags.SYNC_FILTER_TYPE, filter).end(); 4050565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 406277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.end().end().end().done(); // GIE_COLLECTION, GIE_COLLECTIONS, GIE_GET_ITEM_ESTIMATE 407277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank 4080565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank EasResponse resp = mService.sendHttpClientPost("GetItemEstimate", 4090565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank new ByteArrayEntity(s.toByteArray()), EasSyncService.COMMAND_TIMEOUT); 4100565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank int code = resp.getStatus(); 4110565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (code == HttpStatus.SC_OK) { 4120565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (!resp.isEmpty()) { 4130565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank InputStream is = resp.getInputStream(); 4140565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank GetItemEstimateParser gieParser = new GetItemEstimateParser(is); 4150565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank gieParser.parse(); 4160565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Return the estimated number of items 4170565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return gieParser.mEstimate; 4180565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4190565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4200565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If we can't get an estimate, indicate this... 4210565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return -1; 4220565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4230565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 4248efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank /** 4258efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank * Return the value of isLooping() as returned from the parser 4268efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank */ 4278efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank @Override 4288efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank public boolean isLooping() { 4298efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return mIsLooping; 430ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 431147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 432aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank @Override 433aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank public boolean isSyncable() { 434aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank return true; 435aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank } 436aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank 437368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank public class EasEmailSyncParser extends AbstractSyncParser { 438ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 439147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY = 440ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?"; 441ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 442498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final String mMailboxIdAsString; 443ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 444498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Message> newEmails = new ArrayList<Message>(); 445498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Message> fetchedEmails = new ArrayList<Message>(); 446498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Long> deletedEmails = new ArrayList<Long>(); 447498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>(); 44848af7392c82262d17700e3fbdccf3a582809d449Marc Blank 44948af7392c82262d17700e3fbdccf3a582809d449Marc Blank public EasEmailSyncParser(InputStream in, EmailSyncAdapter adapter) throws IOException { 45048af7392c82262d17700e3fbdccf3a582809d449Marc Blank super(in, adapter); 451ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mMailboxIdAsString = Long.toString(mMailbox.mId); 452ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 453ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 45477186bb1a174432ef272584374942d8b9228e39cMarc Blank public EasEmailSyncParser(Parser parser, EmailSyncAdapter adapter) throws IOException { 45526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank super(parser, adapter); 45626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank mMailboxIdAsString = Long.toString(mMailbox.mId); 45726d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 45826d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 45926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank public void addData (Message msg, int endingTag) throws IOException { 460ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<Attachment> atts = new ArrayList<Attachment>(); 4614f15001bdfd11c79524b4e44d60041967779e763Marc Blank boolean truncated = false; 462ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 46326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank while (nextTag(endingTag) != END) { 464ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 4657c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATTACHMENTS: 4665ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up 467d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank attachmentsParser(atts, msg); 468ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4697c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_TO: 47067698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mTo = Address.pack(Address.parse(getValue())); 471ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4727c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FROM: 473705a309bd78efd77469ac90a57849619de3317e3Mihai Preda Address[] froms = Address.parse(getValue()); 474705a309bd78efd77469ac90a57849619de3317e3Mihai Preda if (froms != null && froms.length > 0) { 4754f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mDisplayName = froms[0].toFriendly(); 476ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 477705a309bd78efd77469ac90a57849619de3317e3Mihai Preda msg.mFrom = Address.pack(froms); 478ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4797c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_CC: 48067698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mCc = Address.pack(Address.parse(getValue())); 481ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4827c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_REPLY_TO: 48367698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mReplyTo = Address.pack(Address.parse(getValue())); 484ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4857c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DATE_RECEIVED: 4868e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue()); 487ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4887c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_SUBJECT: 489ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mSubject = getValue(); 490ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4917c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 492ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagRead = getValueInt() == 1; 493ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4947c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_BODY: 49500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyParser(msg); 49600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 4977c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 498ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank msg.mFlagFavorite = flagParser(); 499ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 5004f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_TRUNCATED: 5014f15001bdfd11c79524b4e44d60041967779e763Marc Blank truncated = getValueInt() == 1; 5024f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 5034f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_DATA: 5044f15001bdfd11c79524b4e44d60041967779e763Marc Blank // We get MIME data for EAS 2.5. First we parse it, then we take the 5054f15001bdfd11c79524b4e44d60041967779e763Marc Blank // html and/or plain text data and store it in the message 506068e073ffcb68e06785929208d6a6761b29030f3Marc Blank if (truncated) { 507068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // If the MIME data is truncated, don't bother parsing it, because 508068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // it will take time and throw an exception anyway when EOF is reached 509068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // In this case, we will load the body separately by tagging the message 510068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // "partially loaded". 5113fb34bbdabba3ef5bc5742aa012fd9245944165fMarc Blank // Get the data (and ignore it) 5123fb34bbdabba3ef5bc5742aa012fd9245944165fMarc Blank getValue(); 5134f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Partially loaded: ", msg.mServerId); 5144f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL; 5154f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = true; 516068e073ffcb68e06785929208d6a6761b29030f3Marc Blank } else { 517068e073ffcb68e06785929208d6a6761b29030f3Marc Blank mimeBodyParser(msg, getValue()); 5184f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 5194f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 5207c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_BODY: 52167698e240187c902bed123bf18d342ff25ec75c7Marc Blank String text = getValue(); 52267698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mText = text; 523ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5247531be7774769c84b499b1de5dc46da3a9468316Marc Blank case Tags.EMAIL_MESSAGE_CLASS: 5257531be7774769c84b499b1de5dc46da3a9468316Marc Blank String messageClass = getValue(); 5267531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (messageClass.equals("IPM.Schedule.Meeting.Request")) { 527888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_INVITE; 5287531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) { 529888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_CANCEL; 5307531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 5317531be7774769c84b499b1de5dc46da3a9468316Marc Blank break; 532c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_MEETING_REQUEST: 533c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank meetingRequestParser(msg); 534c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 53577186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.RIGHTS_LICENSE: 53677186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 53777186bb1a174432ef272584374942d8b9228e39cMarc Blank break; 53877186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.EMAIL2_CONVERSATION_ID: 53977186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.EMAIL2_CONVERSATION_INDEX: 54077186bb1a174432ef272584374942d8b9228e39cMarc Blank // Note that the value of these two tags is a byte array 54177186bb1a174432ef272584374942d8b9228e39cMarc Blank getValueBytes(); 54277186bb1a174432ef272584374942d8b9228e39cMarc Blank break; 543422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank case Tags.EMAIL2_LAST_VERB_EXECUTED: 544422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank int val = getValueInt(); 545422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { 546422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank // We aren't required to distinguish between reply and reply all here 547422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank msg.mFlags |= Message.FLAG_REPLIED_TO; 548422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank } else if (val == LAST_VERB_FORWARD) { 549422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank msg.mFlags |= Message.FLAG_FORWARDED; 550422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank } 551422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank break; 552ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 553ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 554ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 555ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 556ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 557ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (atts.size() > 0) { 558ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAttachments = atts; 559ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 560ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 56167698e240187c902bed123bf18d342ff25ec75c7Marc Blank 5625c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank /** 5635c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * Set up the meetingInfo field in the message with various pieces of information gleaned 5645c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * from MeetingRequest tags. This information will be used later to generate an appropriate 5655c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * reply email if the user chooses to respond 5665c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @param msg the Message being built 5675c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @throws IOException 5685c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank */ 569c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void meetingRequestParser(Message msg) throws IOException { 5705c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank PackedString.Builder packedString = new PackedString.Builder(); 571c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_MEETING_REQUEST) != END) { 572c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 5735c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_DTSTAMP: 5745c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTAMP, getValue()); 5755c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 576c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_START_TIME: 5775c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTART, getValue()); 5785c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 5795c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_END_TIME: 5805c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTEND, getValue()); 5815c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 5825c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_ORGANIZER: 5835c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_ORGANIZER_EMAIL, getValue()); 5845c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 5855c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_LOCATION: 5865c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_LOCATION, getValue()); 5875c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 5885c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_GLOBAL_OBJID: 589b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank packedString.put(MeetingInfo.MEETING_UID, 590b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank CalendarUtilities.getUidFromGlobalObjId(getValue())); 591c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 592c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_CATEGORIES: 59377186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 594c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 595c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCES: 596c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank recurrencesParser(); 597c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 59839fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank case Tags.EMAIL_RESPONSE_REQUESTED: 59939fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank packedString.put(MeetingInfo.MEETING_RESPONSE_REQUESTED, getValue()); 60039fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank break; 601c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 602c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 603c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 604c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 6055c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (msg.mSubject != null) { 6065c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_TITLE, msg.mSubject); 6075c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 6085c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank msg.mMeetingInfo = packedString.toString(); 609c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 610c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 611c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void recurrencesParser() throws IOException { 612c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_RECURRENCES) != END) { 613c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 614c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCE: 61577186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 616f69266d6671aa2c55fd04117a36f74dd17f73067Marc Blank break; 617c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 618c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 619c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 620c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 621c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 622c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 623d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank /** 624d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * Parse a message from the server stream. 625d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * @return the parsed Message 626d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * @throws IOException 627d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank */ 62877186bb1a174432ef272584374942d8b9228e39cMarc Blank private Message addParser() throws IOException, CommandStatusException { 629ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Message msg = new Message(); 630ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAccountKey = mAccount.mId; 631ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mMailboxKey = mMailbox.mId; 632f02459a766ddb1727d191daa0aeb559c8f848668Andrew Stadler msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE; 633247d8026d6be4609c4d7a8b7be8f2f5a0908e511Marc Blank // Default to 1 (success) in case we don't get this tag 634247d8026d6be4609c4d7a8b7be8f2f5a0908e511Marc Blank int status = 1; 635ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 6367c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_ADD) != END) { 637ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 6387c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 639ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mServerId = getValue(); 640ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 641d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank case Tags.SYNC_STATUS: 642d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank status = getValueInt(); 643d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank break; 6447c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_APPLICATION_DATA: 64526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank addData(msg, tag); 646ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 647ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 648ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 649ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 650ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 651d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // For sync, status 1 = success 652d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank if (status != 1) { 65377186bb1a174432ef272584374942d8b9228e39cMarc Blank throw new CommandStatusException(status, msg.mServerId); 654d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 655d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank return msg; 656ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 657ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 658ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank // For now, we only care about the "active" state 659ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank private Boolean flagParser() throws IOException { 660ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean state = false; 6617c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_FLAG) != END) { 662ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank switch (tag) { 6637c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG_STATUS: 664ce17455fc5abf061e252d495288d0d56404b0b62Marc Blank state = getValueInt() == 2; 665ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 666ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank default: 667ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank skipTag(); 668ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 669ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 670ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank return state; 671ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 672ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank 67300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void bodyParser(Message msg) throws IOException { 67400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String bodyType = Eas.BODY_PREFERENCE_TEXT; 67500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String body = ""; 6767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_BODY) != END) { 67700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank switch (tag) { 6787c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_TYPE: 67900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyType = getValue(); 68000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 6817c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_DATA: 68200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank body = getValue(); 68300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 68400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank default: 68500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank skipTag(); 68600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 68700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 68800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // We always ask for TEXT or HTML; there's no third option 68900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) { 69000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mHtml = body; 69100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 69200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mText = body; 69300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 69400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 69500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 6964f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 697068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * Parses untruncated MIME data, saving away the text parts 6984f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param msg the message we're building 6994f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param mimeData the MIME data we've received from the server 700068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * @throws IOException 7014f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 702068e073ffcb68e06785929208d6a6761b29030f3Marc Blank private void mimeBodyParser(Message msg, String mimeData) throws IOException { 7034f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 7044f15001bdfd11c79524b4e44d60041967779e763Marc Blank ByteArrayInputStream in = new ByteArrayInputStream(mimeData.getBytes()); 7054f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The constructor parses the message 706068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeMessage mimeMessage = new MimeMessage(in); 707068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // Now process body parts & attachments 708068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> viewables = new ArrayList<Part>(); 709068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // We'll ignore the attachments, as we'll get them directly from EAS 710068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> attachments = new ArrayList<Part>(); 711068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeUtility.collectParts(mimeMessage, viewables, attachments); 712068e073ffcb68e06785929208d6a6761b29030f3Marc Blank Body tempBody = new Body(); 713068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // updateBodyFields fills in the content fields of the Body 714e951b589c5134a1154ec3743d79236dee54a6519Marc Blank ConversionUtilities.updateBodyFields(tempBody, msg, viewables); 715068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // But we need them in the message itself for handling during commit() 716068e073ffcb68e06785929208d6a6761b29030f3Marc Blank msg.mHtml = tempBody.mHtmlContent; 717068e073ffcb68e06785929208d6a6761b29030f3Marc Blank msg.mText = tempBody.mTextContent; 7184f15001bdfd11c79524b4e44d60041967779e763Marc Blank } catch (MessagingException e) { 719068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // This would most likely indicate a broken stream 720068e073ffcb68e06785929208d6a6761b29030f3Marc Blank throw new IOException(e); 7214f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7224f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7234f15001bdfd11c79524b4e44d60041967779e763Marc Blank 7245ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank private void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException { 7255ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENTS) != END) { 7265ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank switch (tag) { 7275ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.EMAIL_ATTACHMENT: 7285ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENT: // BASE_ATTACHMENT is used in EAS 12.0 and up 7295ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank attachmentParser(atts, msg); 7305ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank break; 7315ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank default: 7325ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank skipTag(); 7335ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 7345ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 7355ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 7365ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank 73700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException { 738ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String fileName = null; 739ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String length = null; 740d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank String location = null; 741ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 7427c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENT) != END) { 743ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 7445ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank // We handle both EAS 2.5 and 12.0+ attachments here 7457c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DISPLAY_NAME: 7465ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_DISPLAY_NAME: 747ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank fileName = getValue(); 748ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 7497c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_NAME: 7505ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_FILE_REFERENCE: 751d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank location = getValue(); 752ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 7537c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_SIZE: 7545ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ESTIMATED_DATA_SIZE: 755ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank length = getValue(); 756ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 757ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 758ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 759ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 760ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 761ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 7625ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((fileName != null) && (length != null) && (location != null)) { 763ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Attachment att = new Attachment(); 764ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mEncoding = "base64"; 765ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mSize = Long.parseLong(length); 766ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mFileName = fileName; 767d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank att.mLocation = location; 76800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank att.mMimeType = getMimeTypeFromFileName(fileName); 769057faf66b737bbc7df2eaf77ee7a63827785e1b9Marc Blank att.mAccountKey = mService.mAccount.mId; 770d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank // Check if this attachment can't be downloaded due to an account policy 771d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank if (mPolicy != null) { 772d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank if (mPolicy.mDontAllowAttachments || 773d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank (mPolicy.mMaxAttachmentSize > 0 && 774d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank (att.mSize > mPolicy.mMaxAttachmentSize))) { 775d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank att.mFlags = Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD; 776d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 777d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 778ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank atts.add(att); 779ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagAttachment = true; 780ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 781ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 782ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 78300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /** 784936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * Returns an appropriate mimetype for the given file name's extension. If a mimetype 785936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * cannot be determined, {@code application/<<x>>} [where @{code <<x>> is the extension, 786936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * if it exists or {@code application/octet-stream}]. 78700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * At the moment, this is somewhat lame, since many file types aren't recognized 78800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @param fileName the file name to ponder 78900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 79000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // Note: The MimeTypeMap method currently uses a very limited set of mime types 79100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A bug has been filed against this issue. 79200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank public String getMimeTypeFromFileName(String fileName) { 79300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String mimeType; 79400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank int lastDot = fileName.lastIndexOf('.'); 79500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String extension = null; 7965ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { 79719fd685351de80c62c9bc7f0f05fe96983a8078dMarc Blank extension = fileName.substring(lastDot + 1).toLowerCase(); 79800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 79900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (extension == null) { 80000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A reasonable default for now. 80100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/octet-stream"; 80200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 80300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 80400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (mimeType == null) { 80500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/" + extension; 80600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 80700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 80800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank return mimeType; 80900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 81000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 811ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private Cursor getServerIdCursor(String serverId, String[] projection) { 812c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArguments[0] = serverId; 813c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArguments[1] = mMailboxIdAsString; 814ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return mContentResolver.query(Message.CONTENT_URI, projection, 815c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank WHERE_SERVER_ID_AND_MAILBOX_KEY, mBindArguments, null); 816ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 817ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 818936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 819936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException { 820048d45641b88c87172074aa5f29b3de307bc3712Marc Blank while (nextTag(entryTag) != END) { 821ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 8227c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 823ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = getValue(); 824ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find the message in this mailbox with the given serverId 82518e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank Cursor c = getServerIdCursor(serverId, MESSAGE_ID_SUBJECT_PROJECTION); 826ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 827ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 82849c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank deletes.add(c.getLong(MESSAGE_ID_SUBJECT_ID_COLUMN)); 82918e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank if (Eas.USER_LOG) { 83049c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank userLog("Deleting ", serverId + ", " 83149c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank + c.getString(MESSAGE_ID_SUBJECT_SUBJECT_COLUMN)); 83218e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank } 833ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 834ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 835ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 836ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 837ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 838ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 839ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 840ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 841ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 842ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 843ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 844936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 845ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank class ServerChange { 8469b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final long id; 8479b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Boolean read; 8489b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Boolean flag; 8499b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Integer flags; 850ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 8519b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank ServerChange(long _id, Boolean _read, Boolean _flag, Integer _flags) { 852ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = _id; 853ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = _read; 854ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = _flag; 8559b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = _flags; 856ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 857ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 858ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 859936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 860936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy void changeParser(ArrayList<ServerChange> changes) throws IOException { 861ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = null; 862ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldRead = false; 863ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldFlag = false; 8649b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank int flags = 0; 865ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = 0; 8667c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_CHANGE) != END) { 867ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 8687c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 869ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank serverId = getValue(); 870ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = getServerIdCursor(serverId, Message.LIST_PROJECTION); 871ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 872ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 8730a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Changing ", serverId); 874ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank oldRead = c.getInt(Message.LIST_READ_COLUMN) == Message.READ; 875ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank oldFlag = c.getInt(Message.LIST_FAVORITE_COLUMN) == 1; 8769b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = c.getInt(Message.LIST_FLAGS_COLUMN); 877ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = c.getLong(Message.LIST_ID_COLUMN); 878ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 879ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 880ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 881ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 882ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 883d82abe7213806e478338af4202b3622f34b5d6feMarc Blank case Tags.SYNC_APPLICATION_DATA: 8849b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank changeApplicationDataParser(changes, oldRead, oldFlag, flags, id); 885d82abe7213806e478338af4202b3622f34b5d6feMarc Blank break; 886d82abe7213806e478338af4202b3622f34b5d6feMarc Blank default: 887d82abe7213806e478338af4202b3622f34b5d6feMarc Blank skipTag(); 888d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 889d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 890d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 891d82abe7213806e478338af4202b3622f34b5d6feMarc Blank 892d82abe7213806e478338af4202b3622f34b5d6feMarc Blank private void changeApplicationDataParser(ArrayList<ServerChange> changes, Boolean oldRead, 8939b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank Boolean oldFlag, int oldFlags, long id) throws IOException { 894d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean read = null; 895d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean flag = null; 8969b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank Integer flags = null; 897d82abe7213806e478338af4202b3622f34b5d6feMarc Blank while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 898d82abe7213806e478338af4202b3622f34b5d6feMarc Blank switch (tag) { 8997c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 900ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = getValueInt() == 1; 901ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 9027c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 903ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = flagParser(); 904ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 9059b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank case Tags.EMAIL2_LAST_VERB_EXECUTED: 9069b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank int val = getValueInt(); 9079b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // Clear out the old replied/forward flags and add in the new flag 9089b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = oldFlags & ~(Message.FLAG_REPLIED_TO | Message.FLAG_FORWARDED); 9099b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { 9109b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // We aren't required to distinguish between reply and reply all here 9119b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags |= Message.FLAG_REPLIED_TO; 9129b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } else if (val == LAST_VERB_FORWARD) { 9139b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags |= Message.FLAG_FORWARDED; 9149b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } 9159b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank break; 916ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 917ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 918ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 919ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 9209b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // See if there are flag changes re: read, flag (favorite) or replied/forwarded 9215ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if (((read != null) && !oldRead.equals(read)) || 9229b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank ((flag != null) && !oldFlag.equals(flag)) || (flags != null)) { 9239b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank changes.add(new ServerChange(id, read, flag, flags)); 924ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 925ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 926ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 92700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /* (non-Javadoc) 92800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @see com.android.exchange.adapter.EasContentParser#commandsParser() 92900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 930147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank @Override 93177186bb1a174432ef272584374942d8b9228e39cMarc Blank public void commandsParser() throws IOException, CommandStatusException { 9327c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_COMMANDS) != END) { 9337c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank if (tag == Tags.SYNC_ADD) { 934d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank newEmails.add(addParser()); 9350a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 936048d45641b88c87172074aa5f29b3de307bc3712Marc Blank } else if (tag == Tags.SYNC_DELETE || tag == Tags.SYNC_SOFT_DELETE) { 937048d45641b88c87172074aa5f29b3de307bc3712Marc Blank deleteParser(deletedEmails, tag); 9380a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 9397c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_CHANGE) { 940ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank changeParser(changedEmails); 9410a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 942ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else 943ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 944ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 94548af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 94648af7392c82262d17700e3fbdccf3a582809d449Marc Blank 94748af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 9484f15001bdfd11c79524b4e44d60041967779e763Marc Blank public void responsesParser() throws IOException { 9494f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (nextTag(Tags.SYNC_RESPONSES) != END) { 9504f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (tag == Tags.SYNC_ADD || tag == Tags.SYNC_CHANGE || tag == Tags.SYNC_DELETE) { 9514f15001bdfd11c79524b4e44d60041967779e763Marc Blank // We can ignore all of these 9524f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else if (tag == Tags.SYNC_FETCH) { 953d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank try { 954d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank fetchedEmails.add(addParser()); 95577186bb1a174432ef272584374942d8b9228e39cMarc Blank } catch (CommandStatusException sse) { 956d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank if (sse.mStatus == 8) { 957d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // 8 = object not found; delete the message from EmailProvider 958d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // No other status should be seen in a fetch response, except, perhaps, 959d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // for some temporary server failure 96077186bb1a174432ef272584374942d8b9228e39cMarc Blank mBindArguments[0] = sse.mItemId; 961d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank mBindArguments[1] = mMailboxIdAsString; 962d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank mContentResolver.delete(Message.CONTENT_URI, 963d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank WHERE_SERVER_ID_AND_MAILBOX_KEY, mBindArguments); 964d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 965d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 9664f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9674f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 96848af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 96948af7392c82262d17700e3fbdccf3a582809d449Marc Blank 97048af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 971a1e1f139046dfb28ea69c46d392f70764ad6f822Andrew Stadler public void commit() { 972ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Use a batch operation to handle the changes 973ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // TODO New mail notifications? Who looks for these? 974ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 9754f15001bdfd11c79524b4e44d60041967779e763Marc Blank 9764f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (Message msg: fetchedEmails) { 9774f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find the original message's id (by serverId and mailbox) 9784f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = getServerIdCursor(msg.mServerId, EmailContent.ID_PROJECTION); 9794f15001bdfd11c79524b4e44d60041967779e763Marc Blank String id = null; 9804f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 9814f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (c.moveToFirst()) { 9824f15001bdfd11c79524b4e44d60041967779e763Marc Blank id = c.getString(EmailContent.ID_PROJECTION_COLUMN); 9834f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9844f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 9854f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 9864f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9874f15001bdfd11c79524b4e44d60041967779e763Marc Blank 9884f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we find one, we do two things atomically: 1) set the body text for the 9894f15001bdfd11c79524b4e44d60041967779e763Marc Blank // message, and 2) mark the message loaded (i.e. completely loaded) 9904f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (id != null) { 9914f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Fetched body successfully for ", id); 9924f15001bdfd11c79524b4e44d60041967779e763Marc Blank mBindArgument[0] = id; 9934f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Body.CONTENT_URI) 9944f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withSelection(Body.MESSAGE_KEY + "=?", mBindArgument) 9954f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withValue(Body.TEXT_CONTENT, msg.mText) 9964f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 9974f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Message.CONTENT_URI) 9984f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withSelection(EmailContent.RECORD_ID + "=?", mBindArgument) 999068e073ffcb68e06785929208d6a6761b29030f3Marc Blank .withValue(Message.FLAG_LOADED, Message.FLAG_LOADED_COMPLETE) 10004f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 10014f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 10024f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 10034f15001bdfd11c79524b4e44d60041967779e763Marc Blank 100477424af660458104b732bdcb718874b17d0cab3aMarc Blank for (Message msg: newEmails) { 100577424af660458104b732bdcb718874b17d0cab3aMarc Blank msg.addSaveOps(ops); 1006ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 10074f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1008ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (Long id : deletedEmails) { 1009ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newDelete( 1010ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, id)).build()); 1011e951b589c5134a1154ec3743d79236dee54a6519Marc Blank AttachmentUtilities.deleteAllAttachmentFiles(mContext, mAccount.mId, id); 1012ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 10134f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1014ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!changedEmails.isEmpty()) { 1015ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Server wins in a conflict... 1016ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (ServerChange change : changedEmails) { 1017ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank ContentValues cv = new ContentValues(); 1018ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.read != null) { 1019ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_READ, change.read); 1020ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 1021ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.flag != null) { 1022ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_FAVORITE, change.flag); 1023ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 10249b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank if (change.flags != null) { 10259b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank cv.put(MessageColumns.FLAGS, change.flags); 10269b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } 1027ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 1028ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, change.id)) 1029ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .withValues(cv) 1030ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .build()); 1031ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1032ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 10338d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank 10348d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // We only want to update the sync key here 10358d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentValues mailboxValues = new ContentValues(); 10368d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank mailboxValues.put(Mailbox.SYNC_KEY, mMailbox.mSyncKey); 1037ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 10388d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId)) 10398d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank .withValues(mailboxValues).build()); 1040ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 10418480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank addCleanupOps(ops); 1042147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 10431b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // No commits if we're stopped 10441b275b9408d5b856e2482fa3951827489e9585ccMarc Blank synchronized (mService.getSynchronizer()) { 10451b275b9408d5b856e2482fa3951827489e9585ccMarc Blank if (mService.isStopped()) return; 10461b275b9408d5b856e2482fa3951827489e9585ccMarc Blank try { 1047c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank mContentResolver.applyBatch(EmailContent.AUTHORITY, ops); 10480a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey); 10491b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } catch (RemoteException e) { 10501b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // There is nothing to be done here; fail by returning null 10511b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } catch (OperationApplicationException e) { 10521b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // There is nothing to be done here; fail by returning null 10531b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } 1054ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 105577424af660458104b732bdcb718874b17d0cab3aMarc Blank } 1056ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1057ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1058ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 1059ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String getCollectionName() { 1060ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Email"; 1061ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1062ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 10638480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank private void addCleanupOps(ArrayList<ContentProviderOperation> ops) { 10648480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // If we've sent local deletions, clear out the deleted table 10658480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mDeletedIdList) { 10668480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 10678480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.DELETED_CONTENT_URI, id)).build()); 10688480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 10698480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // And same with the updates 10708480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mUpdatedIdList) { 10718480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 10728480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI, id)).build()); 10738480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 1074a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank // Delete any moved messages (since we've just synced the mailbox, and no longer need the 1075a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank // placeholder message); this prevents duplicates from appearing in the mailbox. 1076a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank mBindArgument[0] = Long.toString(mMailbox.mId); 1077a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank ops.add(ContentProviderOperation.newDelete(Message.CONTENT_URI) 1078a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank .withSelection(WHERE_MAILBOX_KEY_AND_MOVED, mBindArgument).build()); 10798480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 10808480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 10818480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank @Override 108248af7392c82262d17700e3fbdccf3a582809d449Marc Blank public void cleanup() { 10838480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank if (!mDeletedIdList.isEmpty() || !mUpdatedIdList.isEmpty()) { 10848480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 10858480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank addCleanupOps(ops); 10868480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank try { 108748af7392c82262d17700e3fbdccf3a582809d449Marc Blank mContext.getContentResolver() 1088c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank .applyBatch(EmailContent.AUTHORITY, ops); 10898480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } catch (RemoteException e) { 10908480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // There is nothing to be done here; fail by returning null 10918480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } catch (OperationApplicationException e) { 10928480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // There is nothing to be done here; fail by returning null 10938480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 10948480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 10958480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 10968480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 109736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private String formatTwo(int num) { 109836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (num < 10) { 109936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return "0" + (char)('0' + num); 110036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else 110136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return Integer.toString(num); 110236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 110336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 110436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank /** 110536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * Create date/time in RFC8601 format. Oddly enough, for calendar date/time, Microsoft uses 110636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * a different format that excludes the punctuation (this is why I'm not putting this in a 110736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * parent class) 110836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank */ 110936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank public String formatDateTime(Calendar calendar) { 111036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank StringBuilder sb = new StringBuilder(); 111136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank //YYYY-MM-DDTHH:MM:SS.MSSZ 111236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(calendar.get(Calendar.YEAR)); 111336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 111436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MONTH) + 1)); 111536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 111636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.DAY_OF_MONTH))); 111736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('T'); 111836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.HOUR_OF_DAY))); 111936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 112036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MINUTE))); 112136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 112236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.SECOND))); 112336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(".000Z"); 112436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return sb.toString(); 112536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 112636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 1127c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /** 1128c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Note that messages in the deleted database preserve the message's unique id; therefore, we 1129c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * can utilize this id to find references to the message. The only reference situation at this 1130c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * point is in the Body table; it is when sending messages via SmartForward and SmartReply 1131c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 1132c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private boolean messageReferenced(ContentResolver cr, long id) { 1133c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArgument[0] = Long.toString(id); 1134c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // See if this id is referenced in a body 1135c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Body.CONTENT_URI, Body.ID_PROJECTION, WHERE_BODY_SOURCE_MESSAGE_KEY, 1136c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArgument, null); 1137c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank try { 1138c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return c.moveToFirst(); 1139c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } finally { 1140c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank c.close(); 11415acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 1142c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1143c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1144c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /*private*/ /** 1145c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Serialize commands to delete items from the server; as we find items to delete, add their 1146c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * id's to the deletedId's array 1147c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * 1148c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param s the Serializer we're using to create post data 1149c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param deletedIds ids whose deletions are being sent to the server 1150c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param first whether or not this is the first command being sent 1151c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @return true if SYNC_COMMANDS hasn't been sent (false otherwise) 1152c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @throws IOException 1153c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 1154936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 1155c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean sendDeletedItems(Serializer s, ArrayList<Long> deletedIds, boolean first) 1156c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank throws IOException { 1157c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 11585acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank 1159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find any of our deleted items 1160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = cr.query(Message.DELETED_CONTENT_URI, Message.LIST_PROJECTION, 1161ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 1162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We keep track of the list of deleted item id's so that we can remove them from the 1163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // deleted table after the server receives our command 1164c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.clear(); 1165ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1166ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 11675acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = c.getString(Message.LIST_SERVER_ID_COLUMN); 11685acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 11695acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 11705acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 1171c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Also check if this message is referenced elsewhere 1172c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } else if (messageReferenced(cr, c.getLong(Message.CONTENT_ID_COLUMN))) { 1173c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank userLog("Postponing deletion of referenced message: ", serverId); 1174c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank continue; 11755acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } else if (first) { 11767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1177ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank first = false; 1178ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1179ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Send the command to delete this message 11805acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 1181c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.add(c.getLong(Message.LIST_ID_COLUMN)); 1182ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1184ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1185ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1186ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1187c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return first; 1188c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1189c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1190c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank @Override 1191c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank public boolean sendLocalChanges(Serializer s) throws IOException { 1192c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 1193c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1194a261805b03b853cce662b679da3e16120d521b7eMarc Blank if (getSyncKey().equals("0")) { 1195a261805b03b853cce662b679da3e16120d521b7eMarc Blank return false; 1196a261805b03b853cce662b679da3e16120d521b7eMarc Blank } 1197a261805b03b853cce662b679da3e16120d521b7eMarc Blank 1198c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Never upsync from these folders 1199c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (mMailbox.mType == Mailbox.TYPE_DRAFTS || mMailbox.mType == Mailbox.TYPE_OUTBOX) { 1200c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return false; 1201c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1202c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1203c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // This code is split out for unit testing purposes 1204c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean firstCommand = sendDeletedItems(s, mDeletedIdList, true); 1205c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 12064f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (!mFetchRequestList.isEmpty()) { 12074f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Add FETCH commands for messages that need a body (i.e. we didn't find it during 12084f15001bdfd11c79524b4e44d60041967779e763Marc Blank // our earlier sync; this happens only in EAS 2.5 where the body couldn't be found 12094f15001bdfd11c79524b4e44d60041967779e763Marc Blank // after parsing the message's MIME data) 12104f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (firstCommand) { 12114f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_COMMANDS); 12124f15001bdfd11c79524b4e44d60041967779e763Marc Blank firstCommand = false; 12134f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 12144f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (FetchRequest req: mFetchRequestList) { 12154f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_FETCH).data(Tags.SYNC_SERVER_ID, req.serverId).end(); 12164f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 12174f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 12184f15001bdfd11c79524b4e44d60041967779e763Marc Blank 121991e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // Find our trash mailbox, since deletions will have been moved there... 122091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank long trashMailboxId = 12210a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank Mailbox.findMailboxOfType(mContext, mMailbox.mAccountKey, Mailbox.TYPE_TRASH); 122291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 1223ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Do the same now for updated items 1224c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION, 1225ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 122691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 122791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // We keep track of the list of updated item id's as we did above with deleted items 1228ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.clear(); 1229ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1230ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 1231ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = c.getLong(Message.LIST_ID_COLUMN); 1232ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Say we've handled this update 1233ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.add(id); 1234ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We have the id of the changed item. But first, we have to find out its current 1235ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // state, since the updated table saves the opriginal state 1236ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor currentCursor = cr.query(ContentUris.withAppendedId(Message.CONTENT_URI, id), 1237ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank UPDATES_PROJECTION, null, null, null); 1238ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1239ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If this item no longer exists (shouldn't be possible), just move along 1240ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!currentCursor.moveToFirst()) { 1241ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 1242ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 12435acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 12445acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = currentCursor.getString(UPDATES_SERVER_ID_COLUMN); 12455acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 12465acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 12475acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 124891e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // If the message is now in the trash folder, it has been deleted by the user 124991e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank if (currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN) == trashMailboxId) { 1250c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (firstCommand) { 12517c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1252c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank firstCommand = false; 125391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank } 125491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // Send the command to delete this message 12555acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 125691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank continue; 125791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank } 125891e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 125936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean flagChange = false; 126036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean readChange = false; 126136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 12624471a6960d352242cc65bddf7888cc5335840c74Marc Blank long mailbox = currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN); 12634471a6960d352242cc65bddf7888cc5335840c74Marc Blank if (mailbox != c.getLong(Message.LIST_MAILBOX_KEY_COLUMN)) { 12644471a6960d352242cc65bddf7888cc5335840c74Marc Blank // The message has moved to another mailbox; add a request for this 12654471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Note: The Sync command doesn't handle moving messages, so we need 12664471a6960d352242cc65bddf7888cc5335840c74Marc Blank // to handle this as a "request" (similar to meeting response and 12674471a6960d352242cc65bddf7888cc5335840c74Marc Blank // attachment load) 12684471a6960d352242cc65bddf7888cc5335840c74Marc Blank mService.addRequest(new MessageMoveRequest(id, mailbox)); 12694471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Regardless of other changes that might be made, we don't want to indicate 12704471a6960d352242cc65bddf7888cc5335840c74Marc Blank // that this message has been updated until the move request has been 12714471a6960d352242cc65bddf7888cc5335840c74Marc Blank // handled (without this, a crash between the flag upsync and the move 12724471a6960d352242cc65bddf7888cc5335840c74Marc Blank // would cause the move to be lost) 12734471a6960d352242cc65bddf7888cc5335840c74Marc Blank mUpdatedIdList.remove(id); 12744471a6960d352242cc65bddf7888cc5335840c74Marc Blank } 127536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 127636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // We can only send flag changes to the server in 12.0 or later 12774471a6960d352242cc65bddf7888cc5335840c74Marc Blank int flag = 0; 1278d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 127936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flag = currentCursor.getInt(UPDATES_FLAG_COLUMN); 128036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != c.getInt(Message.LIST_FAVORITE_COLUMN)) { 128136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flagChange = true; 128236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 128336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 128436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 128591e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank int read = currentCursor.getInt(UPDATES_READ_COLUMN); 128676eb7b252fc410f5f5d4e90ad54d4bde837de0aaMarc Blank if (read != c.getInt(Message.LIST_READ_COLUMN)) { 128736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank readChange = true; 128836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 128936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 129036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (!flagChange && !readChange) { 129136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // In this case, we've got nothing to send to the server 1292ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 1293ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 129436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 1295c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (firstCommand) { 12967c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1297c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank firstCommand = false; 1298ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 129936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Send the change to "read" and "favorite" (flagged) 13007c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_CHANGE) 13017c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.SYNC_SERVER_ID, c.getString(Message.LIST_SERVER_ID_COLUMN)) 130236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank .start(Tags.SYNC_APPLICATION_DATA); 130336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (readChange) { 130436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_READ, Integer.toString(read)); 130536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 130636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "Flag" is a relatively complex concept in EAS 12.0 and above. It is not only 130736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // the boolean "favorite" that we think of in Gmail, but it also represents a 130836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // follow up action, which can include a subject, start and due dates, and even 130936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // recurrences. We don't support any of this as yet, but EAS 12.0 and higher 131036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // require that a flag contain a status, a type, and four date fields, two each 131136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // for start date and end (due) date. 131236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flagChange) { 131336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != 0) { 131436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Status 2 = set flag 131536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.start(Tags.EMAIL_FLAG).data(Tags.EMAIL_FLAG_STATUS, "2"); 131636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "FollowUp" is the standard type 131736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_FLAG_TYPE, "FollowUp"); 131836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank long now = System.currentTimeMillis(); 131936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank Calendar calendar = 132036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT")); 132136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now); 132236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Flags are required to have a start date and end date (duplicated) 132336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // First, we'll set the current date/time in GMT as the start time 132436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank String utc = formatDateTime(calendar); 132536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_START_DATE, utc).data(Tags.TASK_UTC_START_DATE, utc); 132636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // And then we'll use one week from today for completion date 132736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now + 1*WEEKS); 132836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank utc = formatDateTime(calendar); 132936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_DUE_DATE, utc).data(Tags.TASK_UTC_DUE_DATE, utc); 133036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end(); 133136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else { 133236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.tag(Tags.EMAIL_FLAG); 133336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 133436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 133536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE 1336ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank currentCursor.close(); 1338ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1339ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1340ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1341ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1342ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1343ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1344c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (!firstCommand) { 13457c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.end(); // SYNC_COMMANDS 1346ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1347ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return false; 1348ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank} 1350