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 18c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu// TODO: Deprecated, remove this file. 19c640541ba09b4ecd88f4fa9603dc2d9d474e7996Yu Ping Hu 20ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpackage com.android.exchange.adapter; 21ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 229b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentProviderOperation; 239b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentResolver; 249b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentUris; 259b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentValues; 26c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Huimport android.content.Context; 279b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.OperationApplicationException; 289b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.database.Cursor; 290dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blankimport android.net.Uri; 309b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.os.RemoteException; 31527b5952ef438e010ed5a203f12b58a8a2909338Marc Blankimport android.os.TransactionTooLargeException; 32e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport android.provider.CalendarContract.Events; 33e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport android.text.Html; 34e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport android.text.SpannedString; 35ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blankimport android.text.TextUtils; 36b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugoimport android.util.Base64; 379b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.webkit.MimeTypeMap; 389b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank 39c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.internet.MimeMessage; 40c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.internet.MimeUtility; 41c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.Address; 42c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.MeetingInfo; 43c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.MessagingException; 44c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.PackedString; 45c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.Part; 460dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blankimport com.android.emailcommon.provider.Account; 47c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent; 480dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns; 49c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Attachment; 50c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Body; 510565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport com.android.emailcommon.provider.EmailContent.MailboxColumns; 52c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Message; 53c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.MessageColumns; 54c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.SyncColumns; 554d8774462ace9a45154b2df418b9f2fe7a9c685dBen Komaloimport com.android.emailcommon.provider.Mailbox; 56d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blankimport com.android.emailcommon.provider.Policy; 5734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blankimport com.android.emailcommon.provider.ProviderUnavailableException; 58e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.service.SyncWindow; 59e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.utility.AttachmentUtilities; 60e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.utility.ConversionUtilities; 61e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport com.android.emailcommon.utility.TextUtilities; 62c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.utility.Utility; 6377186bb1a174432ef272584374942d8b9228e39cMarc Blankimport com.android.exchange.CommandStatusException; 6400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.Eas; 65498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport com.android.exchange.EasResponse; 6600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.EasSyncService; 674471a6960d352242cc65bddf7888cc5335840c74Marc Blankimport com.android.exchange.MessageMoveRequest; 680565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport com.android.exchange.R; 69c10a3beef4f048292e6a4ceb31527c5123801517Marc Blankimport com.android.exchange.utility.CalendarUtilities; 70942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedyimport com.android.mail.utils.LogUtils; 71277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blankimport com.google.common.annotations.VisibleForTesting; 72ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 730565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport org.apache.http.HttpStatus; 740565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport org.apache.http.entity.ByteArrayEntity; 750565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 764f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport java.io.ByteArrayInputStream; 7700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException; 7800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream; 7900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList; 8036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blankimport java.util.Calendar; 8100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.GregorianCalendar; 8200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.TimeZone; 83ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 84ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/** 85ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Sync adapter for EAS email 86ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 87ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 887c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankpublic class EmailSyncAdapter extends AbstractSyncAdapter { 8900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 90110837ebff288a75f9bda067c38e2c46797d99b5Alon Albert private static final String TAG = Eas.LOG_TAG; 9169c1a51ac81fe2c649e5f2069dc75565d868db98Ben Komalo 9291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_READ_COLUMN = 0; 9391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_MAILBOX_KEY_COLUMN = 1; 9491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_SERVER_ID_COLUMN = 2; 9536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private static final int UPDATES_FLAG_COLUMN = 3; 9691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final String[] UPDATES_PROJECTION = 9736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank {MessageColumns.FLAG_READ, MessageColumns.MAILBOX_KEY, SyncColumns.SERVER_ID, 9836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank MessageColumns.FLAG_FAVORITE}; 9949c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank 10049c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_ID_COLUMN = 0; 10149c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_SUBJECT_COLUMN = 1; 10218e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank private static final String[] MESSAGE_ID_SUBJECT_PROJECTION = 10318e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank new String[] { Message.RECORD_ID, MessageColumns.SUBJECT }; 10418e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank 105c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private static final String WHERE_BODY_SOURCE_MESSAGE_KEY = Body.SOURCE_MESSAGE_KEY + "=?"; 106a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank private static final String WHERE_MAILBOX_KEY_AND_MOVED = 107a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank MessageColumns.MAILBOX_KEY + "=? AND (" + MessageColumns.FLAGS + "&" + 108a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank EasSyncService.MESSAGE_FLAG_MOVED_MESSAGE + ")!=0"; 1094f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String[] FETCH_REQUEST_PROJECTION = 1104f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {EmailContent.RECORD_ID, SyncColumns.SERVER_ID}; 1114f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_RECORD_ID = 0; 1124f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_SERVER_ID = 1; 1134f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1144f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String EMAIL_WINDOW_SIZE = "5"; 11591e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 1169b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1179b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_REPLY = 1; 1189b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1199b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_REPLY_ALL = 2; 1209b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1219b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_FORWARD = 3; 122422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank 123936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 124936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy ArrayList<Long> mDeletedIdList = new ArrayList<Long>(); 125936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 126936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy ArrayList<Long> mUpdatedIdList = new ArrayList<Long>(); 127498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<FetchRequest> mFetchRequestList = new ArrayList<FetchRequest>(); 128ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1298efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Holds the parser's value for isLooping() 130936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private boolean mIsLooping = false; 1318efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 1326f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank public EmailSyncAdapter(EasSyncService service) { 1336f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank super(service); 1346f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank } 1356f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank 1366f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank @Override 1376f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank public void wipe() { 1386f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.CONTENT_URI, 1396f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 1406f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.DELETED_CONTENT_URI, 1416f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 1426f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.UPDATED_CONTENT_URI, 1436f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 144057faf66b737bbc7df2eaf77ee7a63827785e1b9Marc Blank mService.clearRequests(); 145a261805b03b853cce662b679da3e16120d521b7eMarc Blank mFetchRequestList.clear(); 146134346f5b886e6b53074238546653cdc76bbe868Marc Blank // Delete attachments... 147c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext, mAccount.mId, mMailbox.mId); 148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1504f15001bdfd11c79524b4e44d60041967779e763Marc Blank private String getEmailFilter() { 1518bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank int syncLookback = mMailbox.mSyncLookback; 15235b892c7570b06e72c97c0915d0e41abc83ed6b0Yu Ping Hu if (syncLookback == SyncWindow.SYNC_WINDOW_ACCOUNT 153b43c40606146babc767475bbabac5820efd4c604Marc Blank || mMailbox.mType == Mailbox.TYPE_INBOX) { 1548bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank syncLookback = mAccount.mSyncLookback; 1558bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank } 1568bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank switch (syncLookback) { 157e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_DAY: 1584f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_DAY; 159e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_3_DAYS: 1604f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_3_DAYS; 161e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_WEEK: 1624f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 163e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_2_WEEKS: 1644f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_2_WEEKS; 165e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_MONTH: 1664f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_MONTH; 167e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_ALL: 1684f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_ALL; 1694f15001bdfd11c79524b4e44d60041967779e763Marc Blank default: 1709b65e23aebade9eb057070af3757f490a06148d3Yu Ping Hu // Auto window is deprecated and will also use the default. 1714f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 1724f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1734f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1744f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1754f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 1764f15001bdfd11c79524b4e44d60041967779e763Marc Blank * Holder for fetch request information (record id and server id) 1774f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 178936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private static class FetchRequest { 179936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @SuppressWarnings("unused") 1804f15001bdfd11c79524b4e44d60041967779e763Marc Blank final long messageId; 1814f15001bdfd11c79524b4e44d60041967779e763Marc Blank final String serverId; 1824f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1834f15001bdfd11c79524b4e44d60041967779e763Marc Blank FetchRequest(long _messageId, String _serverId) { 1844f15001bdfd11c79524b4e44d60041967779e763Marc Blank messageId = _messageId; 1854f15001bdfd11c79524b4e44d60041967779e763Marc Blank serverId = _serverId; 1864f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1874f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1884f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1894f15001bdfd11c79524b4e44d60041967779e763Marc Blank @Override 190e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank public void sendSyncOptions(Double protocolVersion, Serializer s, boolean initialSync) 1914f15001bdfd11c79524b4e44d60041967779e763Marc Blank throws IOException { 192e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (initialSync) return; 1934f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.clear(); 1944f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find partially loaded messages; this should typically be a rare occurrence 1954f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = mContext.getContentResolver().query(Message.CONTENT_URI, 1964f15001bdfd11c79524b4e44d60041967779e763Marc Blank FETCH_REQUEST_PROJECTION, 1974f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.FLAG_LOADED + "=" + Message.FLAG_LOADED_PARTIAL + " AND " + 1984f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.MAILBOX_KEY + "=?", 1994f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {Long.toString(mMailbox.mId)}, null); 2004f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 2014f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Put all of these messages into a list; we'll need both id and server id 2024f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (c.moveToNext()) { 2034f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.add(new FetchRequest(c.getLong(FETCH_REQUEST_RECORD_ID), 2044f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.getString(FETCH_REQUEST_SERVER_ID))); 2054f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2064f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 2074f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 2084f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2094f15001bdfd11c79524b4e44d60041967779e763Marc Blank 2104f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The "empty" case is typical; we send a request for changes, and also specify a sync 2114f15001bdfd11c79524b4e44d60041967779e763Marc Blank // window, body preference type (HTML for EAS 12.0 and later; MIME for EAS 2.5), and 2124f15001bdfd11c79524b4e44d60041967779e763Marc Blank // truncation 2134f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If there are fetch requests, we only want the fetches (i.e. no changes from the server) 2144f15001bdfd11c79524b4e44d60041967779e763Marc Blank // so we turn MIME support off. Note that we are always using EAS 2.5 if there are fetch 2154f15001bdfd11c79524b4e44d60041967779e763Marc Blank // requests 2164f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchRequestList.isEmpty()) { 2171d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank // Permanently delete if in trash mailbox 2181d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank // In Exchange 2003, deletes-as-moves tag = true; no tag = false 2191d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank // In Exchange 2007 and up, deletes-as-moves tag is "0" (false) or "1" (true) 2201d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank boolean isTrashMailbox = mMailbox.mType == Mailbox.TYPE_TRASH; 2211d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank if (protocolVersion < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 2221d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank if (!isTrashMailbox) { 2231d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank s.tag(Tags.SYNC_DELETES_AS_MOVES); 2241d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank } 2251d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank } else { 2261d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank s.data(Tags.SYNC_DELETES_AS_MOVES, isTrashMailbox ? "0" : "1"); 2271d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank } 2284f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.tag(Tags.SYNC_GET_CHANGES); 2294f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_WINDOW_SIZE, EMAIL_WINDOW_SIZE); 2304f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 2314f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the lookback appropriately (EAS calls this a "filter") 2329b65e23aebade9eb057070af3757f490a06148d3Yu Ping Hu s.data(Tags.SYNC_FILTER_TYPE, getEmailFilter()); 2334f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the truncation amount for all classes 2344f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (protocolVersion >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 2354f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.BASE_BODY_PREFERENCE); 2364f15001bdfd11c79524b4e44d60041967779e763Marc Blank // HTML for email 2374f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML); 2384f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE); 2394f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2404f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 2414f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Use MIME data for EAS 2.5 2424f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_MIME); 2434f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 2444f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2454f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2464f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 2474f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 2484f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Ask for plain text, rather than MIME data. This guarantees that we'll get a usable 2494f15001bdfd11c79524b4e44d60041967779e763Marc Blank // text body 2504f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_TEXT); 2514f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 2524f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2534f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2544f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2554f15001bdfd11c79524b4e44d60041967779e763Marc Blank 256ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 25777186bb1a174432ef272584374942d8b9228e39cMarc Blank public boolean parse(InputStream is) throws IOException, CommandStatusException { 25848af7392c82262d17700e3fbdccf3a582809d449Marc Blank EasEmailSyncParser p = new EasEmailSyncParser(is, this); 2598efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank boolean res = p.parse(); 2608efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Hold on to the parser's value for isLooping() to pass back to the service 2618efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank mIsLooping = p.isLooping(); 2624f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we've need a body fetch, or we've just finished one, return true in order to continue 263c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu if (p.fetchNeeded() || !mFetchRequestList.isEmpty()) { 2644f15001bdfd11c79524b4e44d60041967779e763Marc Blank return true; 2654f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2660565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 2678efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return res; 2688efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank } 2698efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 2709b65e23aebade9eb057070af3757f490a06148d3Yu Ping Hu /** 2719b65e23aebade9eb057070af3757f490a06148d3Yu Ping Hu * This function is no longer used, but keeping it here in case we revive this functionality. 2729b65e23aebade9eb057070af3757f490a06148d3Yu Ping Hu * @throws IOException 2739b65e23aebade9eb057070af3757f490a06148d3Yu Ping Hu */ 2749b65e23aebade9eb057070af3757f490a06148d3Yu Ping Hu @Deprecated 2750565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private void getAutomaticLookback() throws IOException { 2760565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If we're using an auto lookback, check how many items in the past week 2770565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // TODO Make the literal ints below constants once we twiddle them a bit 2780565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank int items = getEstimate(Eas.FILTER_1_WEEK); 2790565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank int lookback; 2800565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (items > 1050) { 2810565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Over 150/day, just use one day (smallest) 2820565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_DAY; 2830565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 350 || (items == -1)) { 2840565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 50-150/day, use 3 days (150 to 450 messages synced) 2850565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_3_DAYS; 2860565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 150) { 2870565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 20-50/day, use 1 week (140 to 350 messages synced) 2880565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_WEEK; 2890565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 75) { 2900565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 10-25/day, use 1 week (140 to 350 messages synced) 2910565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_2_WEEKS; 2920565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items < 5) { 2930565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If there are only a couple, see if it makes sense to get everything 2940565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank items = getEstimate(Eas.FILTER_ALL); 2950565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (items >= 0 && items < 100) { 2960565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_ALL; 2970565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 2980565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_MONTH; 2990565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3000565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3010565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_MONTH; 3020565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3030dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank 30430726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank // Limit lookback to policy limit 30530726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank if (mAccount.mPolicyKey > 0) { 30630726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank Policy policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 30730726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank if (policy != null) { 30830726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank int maxLookback = policy.mMaxEmailLookback; 30930726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank if (maxLookback != 0 && (lookback > policy.mMaxEmailLookback)) { 31030726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank lookback = policy.mMaxEmailLookback; 31130726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank } 31230726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank } 31330726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank } 31430726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank 3150565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Store the new lookback and persist it 3160dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank // TODO Code similar to this is used elsewhere (e.g. MailboxSettings); try to clean this up 3170565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank ContentValues cv = new ContentValues(); 3180dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank Uri uri; 3190dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank if (mMailbox.mType == Mailbox.TYPE_INBOX) { 3200dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank mAccount.mSyncLookback = lookback; 3210dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank cv.put(AccountColumns.SYNC_LOOKBACK, lookback); 3220dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank uri = ContentUris.withAppendedId(Account.CONTENT_URI, mAccount.mId); 3230dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank } else { 3240dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank mMailbox.mSyncLookback = lookback; 3250dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank cv.put(MailboxColumns.SYNC_LOOKBACK, lookback); 3260dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank uri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId); 3270dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank } 3280dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank mContentResolver.update(uri, cv, null, null); 3290dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank 3300565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank CharSequence[] windowEntries = mContext.getResources().getTextArray( 3310565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank R.array.account_settings_mail_window_entries); 332942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.d(TAG, "Auto lookback: " + windowEntries[lookback]); 3330565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3340565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 335936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private static class GetItemEstimateParser extends Parser { 3360565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private int mEstimate = -1; 3370565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3380565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public GetItemEstimateParser(InputStream in) throws IOException { 3390565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank super(in); 3400565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3410565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 342936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @Override 3430565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public boolean parse() throws IOException { 3440565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Loop here through the remaining xml 3450565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(START_DOCUMENT) != END_DOCUMENT) { 3460565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_GET_ITEM_ESTIMATE) { 3470565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseGetItemEstimate(); 3480565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3490565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3500565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3510565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3520565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return true; 3530565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3540565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3550565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseGetItemEstimate() throws IOException { 3560565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_GET_ITEM_ESTIMATE) != END) { 3570565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_RESPONSE) { 3580565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseResponse(); 3590565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3600565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3610565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3620565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3630565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3640565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3650565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseResponse() throws IOException { 3660565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_RESPONSE) != END) { 3670565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_STATUS) { 368942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.d(TAG, "GIE status: " + getValue()); 3690565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_COLLECTION) { 3700565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseCollection(); 3710565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3720565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3730565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3740565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3750565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3760565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3770565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseCollection() throws IOException { 3780565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_COLLECTION) != END) { 3790565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_CLASS) { 380942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.d(TAG, "GIE class: " + getValue()); 3810565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_COLLECTION_ID) { 382942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.d(TAG, "GIE collectionId: " + getValue()); 3830565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_ESTIMATE) { 3840565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank mEstimate = getValueInt(); 385942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.d(TAG, "GIE estimate: " + mEstimate); 3860565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3870565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3880565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3890565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3900565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3910565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3920565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3930565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank /** 3940565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * Return the estimated number of items to be synced in the current mailbox, based on the 3950565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * passed in filter argument 3960565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @param filter an EAS "window" filter 3970565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @return the estimated number of items to be synced, or -1 if unknown 3980565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @throws IOException 3990565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank */ 4000565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private int getEstimate(String filter) throws IOException { 4010565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Serializer s = new Serializer(); 402277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex10 = mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE; 403277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex03 = mService.mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE; 404277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex07 = !ex10 && !ex03; 4050565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 4060565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank String className = getCollectionName(); 4070565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank String syncKey = getSyncKey(); 4080565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank userLog("gie, sending ", className, " syncKey: ", syncKey); 409277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank 4100565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.start(Tags.GIE_GET_ITEM_ESTIMATE).start(Tags.GIE_COLLECTIONS); 4110565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.start(Tags.GIE_COLLECTION); 412277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank if (ex07) { 413277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2007 likes collection id first 414277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 415277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_FILTER_TYPE, filter); 416277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 417277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank } else if (ex03) { 418277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2003 needs the "class" element 4190565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.data(Tags.GIE_CLASS, className); 420277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 421277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 422277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_FILTER_TYPE, filter); 423277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank } else { 424277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2010 requires the filter inside an OPTIONS container and sync key first 425277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 426277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 427277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.start(Tags.SYNC_OPTIONS).data(Tags.SYNC_FILTER_TYPE, filter).end(); 4280565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 429277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.end().end().end().done(); // GIE_COLLECTION, GIE_COLLECTIONS, GIE_GET_ITEM_ESTIMATE 430277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank 4310565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank EasResponse resp = mService.sendHttpClientPost("GetItemEstimate", 4320565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank new ByteArrayEntity(s.toByteArray()), EasSyncService.COMMAND_TIMEOUT); 4336760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank try { 4346760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank int code = resp.getStatus(); 4356760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank if (code == HttpStatus.SC_OK) { 4366760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank if (!resp.isEmpty()) { 4376760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank InputStream is = resp.getInputStream(); 4386760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank GetItemEstimateParser gieParser = new GetItemEstimateParser(is); 4396760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank gieParser.parse(); 4406760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank // Return the estimated number of items 4416760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank return gieParser.mEstimate; 4426760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } 4430565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4446760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } finally { 4456760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank resp.close(); 4460565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4470565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If we can't get an estimate, indicate this... 4480565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return -1; 4490565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4500565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 4518efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank /** 4528efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank * Return the value of isLooping() as returned from the parser 4538efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank */ 4548efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank @Override 4558efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank public boolean isLooping() { 4568efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return mIsLooping; 457ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 458147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 459aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank @Override 460aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank public boolean isSyncable() { 461aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank return true; 462aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank } 463aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank 464c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu public static class EasEmailSyncParser extends AbstractSyncParser { 465ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 466147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY = 467ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?"; 468ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 469498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final String mMailboxIdAsString; 470ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 471498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Message> newEmails = new ArrayList<Message>(); 472498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Message> fetchedEmails = new ArrayList<Message>(); 473498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Long> deletedEmails = new ArrayList<Long>(); 474498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>(); 47548af7392c82262d17700e3fbdccf3a582809d449Marc Blank 476c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu private final Policy mPolicy; 477c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu private boolean mFetchNeeded = false; 478c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu 47948af7392c82262d17700e3fbdccf3a582809d449Marc Blank public EasEmailSyncParser(InputStream in, EmailSyncAdapter adapter) throws IOException { 48048af7392c82262d17700e3fbdccf3a582809d449Marc Blank super(in, adapter); 481ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mMailboxIdAsString = Long.toString(mMailbox.mId); 482c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu if (mAccount.mPolicyKey != 0) { 483c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 484c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } else { 485c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mPolicy = null; 486c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } 487c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } 488c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu 489c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu public EasEmailSyncParser(final Context context, final ContentResolver resolver, 490c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu final InputStream in, final Mailbox mailbox, final Account account) 491c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu throws IOException { 492c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu super(context, resolver, in, mailbox, account); 493c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mMailboxIdAsString = Long.toString(mMailbox.mId); 494c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu if (mAccount.mPolicyKey != 0) { 495c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 496c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } else { 497c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mPolicy = null; 498c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } 499ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 500ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 50177186bb1a174432ef272584374942d8b9228e39cMarc Blank public EasEmailSyncParser(Parser parser, EmailSyncAdapter adapter) throws IOException { 50226d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank super(parser, adapter); 50326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank mMailboxIdAsString = Long.toString(mMailbox.mId); 504c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu if (mAccount.mPolicyKey != 0) { 505c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 506c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } else { 507c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mPolicy = null; 508c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } 509c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } 510c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu 511c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu public boolean fetchNeeded() { 512c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu return mFetchNeeded; 51326d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 51426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 51526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank public void addData (Message msg, int endingTag) throws IOException { 516ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<Attachment> atts = new ArrayList<Attachment>(); 5174f15001bdfd11c79524b4e44d60041967779e763Marc Blank boolean truncated = false; 518ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 51926d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank while (nextTag(endingTag) != END) { 520ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 5217c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATTACHMENTS: 5225ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up 523d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank attachmentsParser(atts, msg); 524ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5257c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_TO: 52667698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mTo = Address.pack(Address.parse(getValue())); 527ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5287c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FROM: 529705a309bd78efd77469ac90a57849619de3317e3Mihai Preda Address[] froms = Address.parse(getValue()); 530705a309bd78efd77469ac90a57849619de3317e3Mihai Preda if (froms != null && froms.length > 0) { 5314f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mDisplayName = froms[0].toFriendly(); 532ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 533705a309bd78efd77469ac90a57849619de3317e3Mihai Preda msg.mFrom = Address.pack(froms); 534ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5357c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_CC: 53667698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mCc = Address.pack(Address.parse(getValue())); 537ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5387c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_REPLY_TO: 53967698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mReplyTo = Address.pack(Address.parse(getValue())); 540ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5417c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DATE_RECEIVED: 5428e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue()); 543ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5447c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_SUBJECT: 545ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mSubject = getValue(); 546ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5477c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 548ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagRead = getValueInt() == 1; 549ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5507c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_BODY: 55100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyParser(msg); 55200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 5537c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 554ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank msg.mFlagFavorite = flagParser(); 555ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 5564f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_TRUNCATED: 5574f15001bdfd11c79524b4e44d60041967779e763Marc Blank truncated = getValueInt() == 1; 5584f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 5594f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_DATA: 5604f15001bdfd11c79524b4e44d60041967779e763Marc Blank // We get MIME data for EAS 2.5. First we parse it, then we take the 5614f15001bdfd11c79524b4e44d60041967779e763Marc Blank // html and/or plain text data and store it in the message 562068e073ffcb68e06785929208d6a6761b29030f3Marc Blank if (truncated) { 563068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // If the MIME data is truncated, don't bother parsing it, because 564068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // it will take time and throw an exception anyway when EOF is reached 565068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // In this case, we will load the body separately by tagging the message 566068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // "partially loaded". 5673fb34bbdabba3ef5bc5742aa012fd9245944165fMarc Blank // Get the data (and ignore it) 5683fb34bbdabba3ef5bc5742aa012fd9245944165fMarc Blank getValue(); 5694f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Partially loaded: ", msg.mServerId); 5704f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL; 5714f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = true; 572068e073ffcb68e06785929208d6a6761b29030f3Marc Blank } else { 573068e073ffcb68e06785929208d6a6761b29030f3Marc Blank mimeBodyParser(msg, getValue()); 5744f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 5754f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 5767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_BODY: 57767698e240187c902bed123bf18d342ff25ec75c7Marc Blank String text = getValue(); 57867698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mText = text; 579ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5807531be7774769c84b499b1de5dc46da3a9468316Marc Blank case Tags.EMAIL_MESSAGE_CLASS: 5817531be7774769c84b499b1de5dc46da3a9468316Marc Blank String messageClass = getValue(); 5827531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (messageClass.equals("IPM.Schedule.Meeting.Request")) { 583888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_INVITE; 5847531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) { 585888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_CANCEL; 5867531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 5877531be7774769c84b499b1de5dc46da3a9468316Marc Blank break; 588c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_MEETING_REQUEST: 589c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank meetingRequestParser(msg); 590c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 591e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank case Tags.EMAIL_THREAD_TOPIC: 592e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank msg.mThreadTopic = getValue(); 593e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank break; 59477186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.RIGHTS_LICENSE: 59577186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 59677186bb1a174432ef272584374942d8b9228e39cMarc Blank break; 59777186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.EMAIL2_CONVERSATION_ID: 598b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo msg.mServerConversationId = 599b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo Base64.encodeToString(getValueBytes(), Base64.URL_SAFE); 600b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo break; 60177186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.EMAIL2_CONVERSATION_INDEX: 602b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo // Ignore this byte array since we're not constructing a tree. 60377186bb1a174432ef272584374942d8b9228e39cMarc Blank getValueBytes(); 60477186bb1a174432ef272584374942d8b9228e39cMarc Blank break; 605422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank case Tags.EMAIL2_LAST_VERB_EXECUTED: 606422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank int val = getValueInt(); 607422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { 608422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank // We aren't required to distinguish between reply and reply all here 609422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank msg.mFlags |= Message.FLAG_REPLIED_TO; 610422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank } else if (val == LAST_VERB_FORWARD) { 611422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank msg.mFlags |= Message.FLAG_FORWARDED; 612422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank } 613422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank break; 614ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 615ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 616ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 617ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 618ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 619ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (atts.size() > 0) { 620ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAttachments = atts; 621ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 622e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 623e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if ((msg.mFlags & Message.FLAG_INCOMING_MEETING_MASK) != 0) { 624e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String text = TextUtilities.makeSnippetFromHtmlText( 625e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank msg.mText != null ? msg.mText : msg.mHtml); 626e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (TextUtils.isEmpty(text)) { 627e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Create text for this invitation 628e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String meetingInfo = msg.mMeetingInfo; 629e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (!TextUtils.isEmpty(meetingInfo)) { 630e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank PackedString ps = new PackedString(meetingInfo); 631e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ContentValues values = new ContentValues(); 632e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank putFromMeeting(ps, MeetingInfo.MEETING_LOCATION, values, 633e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Events.EVENT_LOCATION); 634e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String dtstart = ps.get(MeetingInfo.MEETING_DTSTART); 635e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (!TextUtils.isEmpty(dtstart)) { 636e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank long startTime = Utility.parseEmailDateTimeToMillis(dtstart); 637e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank values.put(Events.DTSTART, startTime); 638e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 639e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank putFromMeeting(ps, MeetingInfo.MEETING_ALL_DAY, values, 640e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Events.ALL_DAY); 641e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank msg.mText = CalendarUtilities.buildMessageTextFromEntityValues( 642e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank mContext, values, null); 643e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank msg.mHtml = Html.toHtml(new SpannedString(msg.mText)); 644e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 645e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 646e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 647e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 648e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 649942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy private static void putFromMeeting(PackedString ps, String field, ContentValues values, 650e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String column) { 651e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String val = ps.get(field); 652e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (!TextUtils.isEmpty(val)) { 653e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank values.put(column, val); 654e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 655ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 65667698e240187c902bed123bf18d342ff25ec75c7Marc Blank 6575c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank /** 6585c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * Set up the meetingInfo field in the message with various pieces of information gleaned 6595c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * from MeetingRequest tags. This information will be used later to generate an appropriate 6605c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * reply email if the user chooses to respond 6615c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @param msg the Message being built 6625c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @throws IOException 6635c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank */ 664c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void meetingRequestParser(Message msg) throws IOException { 6655c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank PackedString.Builder packedString = new PackedString.Builder(); 666c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_MEETING_REQUEST) != END) { 667c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 6685c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_DTSTAMP: 6695c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTAMP, getValue()); 6705c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 671c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_START_TIME: 6725c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTART, getValue()); 6735c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 6745c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_END_TIME: 6755c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTEND, getValue()); 6765c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 6775c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_ORGANIZER: 6785c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_ORGANIZER_EMAIL, getValue()); 6795c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 6805c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_LOCATION: 6815c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_LOCATION, getValue()); 6825c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 6835c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_GLOBAL_OBJID: 684b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank packedString.put(MeetingInfo.MEETING_UID, 685b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank CalendarUtilities.getUidFromGlobalObjId(getValue())); 686c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 687c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_CATEGORIES: 68877186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 689c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 690c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCES: 691c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank recurrencesParser(); 692c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 69339fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank case Tags.EMAIL_RESPONSE_REQUESTED: 69439fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank packedString.put(MeetingInfo.MEETING_RESPONSE_REQUESTED, getValue()); 69539fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank break; 696e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank case Tags.EMAIL_ALL_DAY_EVENT: 697e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (getValueInt() == 1) { 69892f04b67d9907753a91ff2386db93c0e07d76633Marc Blank packedString.put(MeetingInfo.MEETING_ALL_DAY, "1"); 699e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 700e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank break; 701c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 702c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 703c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 704c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 7055c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (msg.mSubject != null) { 7065c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_TITLE, msg.mSubject); 7075c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 7085c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank msg.mMeetingInfo = packedString.toString(); 709c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 710c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 711c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void recurrencesParser() throws IOException { 712c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_RECURRENCES) != END) { 713c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 714c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCE: 71577186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 716f69266d6671aa2c55fd04117a36f74dd17f73067Marc Blank break; 717c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 718c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 719c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 720c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 721c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 722c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 723d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank /** 724d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * Parse a message from the server stream. 725d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * @return the parsed Message 726d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * @throws IOException 727d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank */ 72877186bb1a174432ef272584374942d8b9228e39cMarc Blank private Message addParser() throws IOException, CommandStatusException { 729ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Message msg = new Message(); 730ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAccountKey = mAccount.mId; 731ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mMailboxKey = mMailbox.mId; 732f02459a766ddb1727d191daa0aeb559c8f848668Andrew Stadler msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE; 733247d8026d6be4609c4d7a8b7be8f2f5a0908e511Marc Blank // Default to 1 (success) in case we don't get this tag 734247d8026d6be4609c4d7a8b7be8f2f5a0908e511Marc Blank int status = 1; 735ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 7367c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_ADD) != END) { 737ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 7387c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 739ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mServerId = getValue(); 740ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 741d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank case Tags.SYNC_STATUS: 742d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank status = getValueInt(); 743d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank break; 7447c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_APPLICATION_DATA: 74526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank addData(msg, tag); 746ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 747ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 748ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 749ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 750ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 751d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // For sync, status 1 = success 752d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank if (status != 1) { 75377186bb1a174432ef272584374942d8b9228e39cMarc Blank throw new CommandStatusException(status, msg.mServerId); 754d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 755d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank return msg; 756ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 757ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 758ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank // For now, we only care about the "active" state 759ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank private Boolean flagParser() throws IOException { 760ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean state = false; 7617c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_FLAG) != END) { 762ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank switch (tag) { 7637c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG_STATUS: 764ce17455fc5abf061e252d495288d0d56404b0b62Marc Blank state = getValueInt() == 2; 765ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 766ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank default: 767ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank skipTag(); 768ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 769ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 770ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank return state; 771ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 772ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank 77300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void bodyParser(Message msg) throws IOException { 77400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String bodyType = Eas.BODY_PREFERENCE_TEXT; 77500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String body = ""; 7767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_BODY) != END) { 77700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank switch (tag) { 7787c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_TYPE: 77900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyType = getValue(); 78000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 7817c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_DATA: 78200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank body = getValue(); 78300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 78400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank default: 78500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank skipTag(); 78600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 78700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 78800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // We always ask for TEXT or HTML; there's no third option 78900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) { 79000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mHtml = body; 79100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 79200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mText = body; 79300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 79400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 79500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 7964f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 797068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * Parses untruncated MIME data, saving away the text parts 7984f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param msg the message we're building 7994f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param mimeData the MIME data we've received from the server 800068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * @throws IOException 8014f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 802942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy private static void mimeBodyParser(Message msg, String mimeData) throws IOException { 8034f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 8044f15001bdfd11c79524b4e44d60041967779e763Marc Blank ByteArrayInputStream in = new ByteArrayInputStream(mimeData.getBytes()); 8054f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The constructor parses the message 806068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeMessage mimeMessage = new MimeMessage(in); 807068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // Now process body parts & attachments 808068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> viewables = new ArrayList<Part>(); 809068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // We'll ignore the attachments, as we'll get them directly from EAS 810068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> attachments = new ArrayList<Part>(); 811068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeUtility.collectParts(mimeMessage, viewables, attachments); 812532ff870ef8e4c70ada7b86694fb4c1fd154af30Andrew Sapperstein // parseBodyFields fills in the content fields of the Body 813532ff870ef8e4c70ada7b86694fb4c1fd154af30Andrew Sapperstein ConversionUtilities.BodyFieldData data = 814532ff870ef8e4c70ada7b86694fb4c1fd154af30Andrew Sapperstein ConversionUtilities.parseBodyFields(viewables); 815068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // But we need them in the message itself for handling during commit() 816532ff870ef8e4c70ada7b86694fb4c1fd154af30Andrew Sapperstein msg.setFlags(data.isQuotedReply, data.isQuotedForward); 817532ff870ef8e4c70ada7b86694fb4c1fd154af30Andrew Sapperstein msg.mSnippet = data.snippet; 818532ff870ef8e4c70ada7b86694fb4c1fd154af30Andrew Sapperstein msg.mHtml = data.htmlContent; 819532ff870ef8e4c70ada7b86694fb4c1fd154af30Andrew Sapperstein msg.mText = data.textContent; 8204f15001bdfd11c79524b4e44d60041967779e763Marc Blank } catch (MessagingException e) { 821068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // This would most likely indicate a broken stream 822068e073ffcb68e06785929208d6a6761b29030f3Marc Blank throw new IOException(e); 8234f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 8244f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 8254f15001bdfd11c79524b4e44d60041967779e763Marc Blank 8265ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank private void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException { 8275ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENTS) != END) { 8285ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank switch (tag) { 8295ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.EMAIL_ATTACHMENT: 8305ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENT: // BASE_ATTACHMENT is used in EAS 12.0 and up 8315ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank attachmentParser(atts, msg); 8325ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank break; 8335ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank default: 8345ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank skipTag(); 8355ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 8365ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 8375ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 8385ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank 83900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException { 840ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String fileName = null; 841ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String length = null; 842d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank String location = null; 843ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank boolean isInline = false; 844ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank String contentId = null; 845ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 8467c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENT) != END) { 847ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 8485ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank // We handle both EAS 2.5 and 12.0+ attachments here 8497c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DISPLAY_NAME: 8505ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_DISPLAY_NAME: 851ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank fileName = getValue(); 852ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 8537c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_NAME: 8545ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_FILE_REFERENCE: 855d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank location = getValue(); 856ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 8577c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_SIZE: 8585ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ESTIMATED_DATA_SIZE: 859ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank length = getValue(); 860ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 861ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank case Tags.BASE_IS_INLINE: 862ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank isInline = getValueInt() == 1; 863ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank break; 864ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank case Tags.BASE_CONTENT_ID: 865ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank contentId = getValue(); 866ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank break; 867ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 868ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 869ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 870ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 871ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 8725ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((fileName != null) && (length != null) && (location != null)) { 873ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Attachment att = new Attachment(); 874ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mEncoding = "base64"; 875ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mSize = Long.parseLong(length); 876ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mFileName = fileName; 877d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank att.mLocation = location; 87800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank att.mMimeType = getMimeTypeFromFileName(fileName); 879c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu att.mAccountKey = mAccount.mId; 880ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank // Save away the contentId, if we've got one (for inline images); note that the 881ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank // EAS docs appear to be wrong about the tags used; inline images come with 882ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank // contentId rather than contentLocation, when sent from Ex03, Ex07, and Ex10 883ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank if (isInline && !TextUtils.isEmpty(contentId)) { 884ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank att.mContentId = contentId; 885ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank } 886d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank // Check if this attachment can't be downloaded due to an account policy 887d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank if (mPolicy != null) { 888d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank if (mPolicy.mDontAllowAttachments || 889d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank (mPolicy.mMaxAttachmentSize > 0 && 890d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank (att.mSize > mPolicy.mMaxAttachmentSize))) { 891d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank att.mFlags = Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD; 892d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 893d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 894ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank atts.add(att); 895ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagAttachment = true; 896ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 897ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 898ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 89900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /** 900936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * Returns an appropriate mimetype for the given file name's extension. If a mimetype 901936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * cannot be determined, {@code application/<<x>>} [where @{code <<x>> is the extension, 902936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * if it exists or {@code application/octet-stream}]. 90300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * At the moment, this is somewhat lame, since many file types aren't recognized 90400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @param fileName the file name to ponder 90500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 90600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // Note: The MimeTypeMap method currently uses a very limited set of mime types 90700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A bug has been filed against this issue. 90800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank public String getMimeTypeFromFileName(String fileName) { 90900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String mimeType; 91000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank int lastDot = fileName.lastIndexOf('.'); 91100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String extension = null; 9125ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { 91319fd685351de80c62c9bc7f0f05fe96983a8078dMarc Blank extension = fileName.substring(lastDot + 1).toLowerCase(); 91400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 91500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (extension == null) { 91600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A reasonable default for now. 91700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/octet-stream"; 91800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 91900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 92000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (mimeType == null) { 92100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/" + extension; 92200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 92300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 92400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank return mimeType; 92500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 92600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 927ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private Cursor getServerIdCursor(String serverId, String[] projection) { 92834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank Cursor c = mContentResolver.query(Message.CONTENT_URI, projection, 929c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu WHERE_SERVER_ID_AND_MAILBOX_KEY, new String[] {serverId, mMailboxIdAsString}, 930c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu null); 93134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (c == null) throw new ProviderUnavailableException(); 932bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank if (c.getCount() > 1) { 933bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank userLog("Multiple messages with the same serverId/mailbox: " + serverId); 934bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank } 93534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank return c; 936ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 937ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 938936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 939936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException { 940048d45641b88c87172074aa5f29b3de307bc3712Marc Blank while (nextTag(entryTag) != END) { 941ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 9427c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 943ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = getValue(); 944ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find the message in this mailbox with the given serverId 94518e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank Cursor c = getServerIdCursor(serverId, MESSAGE_ID_SUBJECT_PROJECTION); 946ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 947ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 94849c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank deletes.add(c.getLong(MESSAGE_ID_SUBJECT_ID_COLUMN)); 94918e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank if (Eas.USER_LOG) { 95049c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank userLog("Deleting ", serverId + ", " 95149c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank + c.getString(MESSAGE_ID_SUBJECT_SUBJECT_COLUMN)); 95218e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank } 953ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 954ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 955ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 956ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 957ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 958ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 959ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 960ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 961ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 962ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 963ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 964936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 965ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank class ServerChange { 9669b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final long id; 9679b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Boolean read; 9689b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Boolean flag; 9699b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Integer flags; 970ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 9719b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank ServerChange(long _id, Boolean _read, Boolean _flag, Integer _flags) { 972ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = _id; 973ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = _read; 974ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = _flag; 9759b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = _flags; 976ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 977ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 978ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 979936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 980936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy void changeParser(ArrayList<ServerChange> changes) throws IOException { 981ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = null; 982ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldRead = false; 983ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldFlag = false; 9849b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank int flags = 0; 985ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = 0; 9867c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_CHANGE) != END) { 987ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 9887c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 989ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank serverId = getValue(); 990ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = getServerIdCursor(serverId, Message.LIST_PROJECTION); 991ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 992ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 9930a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Changing ", serverId); 994ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank oldRead = c.getInt(Message.LIST_READ_COLUMN) == Message.READ; 995ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank oldFlag = c.getInt(Message.LIST_FAVORITE_COLUMN) == 1; 9969b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = c.getInt(Message.LIST_FLAGS_COLUMN); 997ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = c.getLong(Message.LIST_ID_COLUMN); 998ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 999ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1000ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1001ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1002ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 1003d82abe7213806e478338af4202b3622f34b5d6feMarc Blank case Tags.SYNC_APPLICATION_DATA: 10049b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank changeApplicationDataParser(changes, oldRead, oldFlag, flags, id); 1005d82abe7213806e478338af4202b3622f34b5d6feMarc Blank break; 1006d82abe7213806e478338af4202b3622f34b5d6feMarc Blank default: 1007d82abe7213806e478338af4202b3622f34b5d6feMarc Blank skipTag(); 1008d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 1009d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 1010d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 1011d82abe7213806e478338af4202b3622f34b5d6feMarc Blank 1012d82abe7213806e478338af4202b3622f34b5d6feMarc Blank private void changeApplicationDataParser(ArrayList<ServerChange> changes, Boolean oldRead, 10139b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank Boolean oldFlag, int oldFlags, long id) throws IOException { 1014d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean read = null; 1015d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean flag = null; 10169b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank Integer flags = null; 1017d82abe7213806e478338af4202b3622f34b5d6feMarc Blank while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 1018d82abe7213806e478338af4202b3622f34b5d6feMarc Blank switch (tag) { 10197c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 1020ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = getValueInt() == 1; 1021ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 10227c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 1023ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = flagParser(); 1024ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 10259b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank case Tags.EMAIL2_LAST_VERB_EXECUTED: 10269b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank int val = getValueInt(); 10279b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // Clear out the old replied/forward flags and add in the new flag 10289b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = oldFlags & ~(Message.FLAG_REPLIED_TO | Message.FLAG_FORWARDED); 10299b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { 10309b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // We aren't required to distinguish between reply and reply all here 10319b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags |= Message.FLAG_REPLIED_TO; 10329b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } else if (val == LAST_VERB_FORWARD) { 10339b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags |= Message.FLAG_FORWARDED; 10349b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } 10359b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank break; 1036ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 1037ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 1038ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1039ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 10409b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // See if there are flag changes re: read, flag (favorite) or replied/forwarded 10415ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if (((read != null) && !oldRead.equals(read)) || 10429b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank ((flag != null) && !oldFlag.equals(flag)) || (flags != null)) { 10439b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank changes.add(new ServerChange(id, read, flag, flags)); 1044ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1045ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1046ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 104700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /* (non-Javadoc) 104800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @see com.android.exchange.adapter.EasContentParser#commandsParser() 104900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 1050147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank @Override 105177186bb1a174432ef272584374942d8b9228e39cMarc Blank public void commandsParser() throws IOException, CommandStatusException { 10527c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_COMMANDS) != END) { 10537c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank if (tag == Tags.SYNC_ADD) { 1054d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank newEmails.add(addParser()); 1055048d45641b88c87172074aa5f29b3de307bc3712Marc Blank } else if (tag == Tags.SYNC_DELETE || tag == Tags.SYNC_SOFT_DELETE) { 1056048d45641b88c87172074aa5f29b3de307bc3712Marc Blank deleteParser(deletedEmails, tag); 10577c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_CHANGE) { 1058ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank changeParser(changedEmails); 1059ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else 1060ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 1061ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 106228f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon 106348af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 106448af7392c82262d17700e3fbdccf3a582809d449Marc Blank 106534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank /** 106634d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank * Removed any messages with status 7 (mismatch) from the updatedIdList 106734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank * @param endTag the tag we end with 106834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank * @throws IOException 106934d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank */ 107034d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank public void failedUpdateParser(int endTag) throws IOException { 107134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank // We get serverId and status in the responses 107234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank String serverId = null; 107334d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank while (nextTag(endTag) != END) { 107434d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (tag == Tags.SYNC_STATUS) { 107534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank int status = getValueInt(); 107634d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (status == 7 && serverId != null) { 107734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank Cursor c = getServerIdCursor(serverId, Message.ID_COLUMN_PROJECTION); 107834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank try { 107934d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (c.moveToFirst()) { 108034d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank Long id = c.getLong(Message.ID_PROJECTION_COLUMN); 1081c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu userLog("Update of " + serverId + " failed; will retry"); 1082c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu //mUpdatedIdList.remove(id); 1083c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu //mService.mUpsyncFailed = true; 108434d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 108534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } finally { 108634d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank c.close(); 108734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 108834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 108934d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } else if (tag == Tags.SYNC_SERVER_ID) { 109034d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank serverId = getValue(); 109134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } else { 109234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank skipTag(); 109334d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 109434d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 109534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 109634d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank 109748af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 10984f15001bdfd11c79524b4e44d60041967779e763Marc Blank public void responsesParser() throws IOException { 10994f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (nextTag(Tags.SYNC_RESPONSES) != END) { 11004f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (tag == Tags.SYNC_ADD || tag == Tags.SYNC_CHANGE || tag == Tags.SYNC_DELETE) { 110134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank failedUpdateParser(tag); 11024f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else if (tag == Tags.SYNC_FETCH) { 1103d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank try { 1104d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank fetchedEmails.add(addParser()); 110577186bb1a174432ef272584374942d8b9228e39cMarc Blank } catch (CommandStatusException sse) { 1106d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank if (sse.mStatus == 8) { 1107d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // 8 = object not found; delete the message from EmailProvider 1108d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // No other status should be seen in a fetch response, except, perhaps, 1109d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // for some temporary server failure 1110d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank mContentResolver.delete(Message.CONTENT_URI, 1111c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu WHERE_SERVER_ID_AND_MAILBOX_KEY, 1112c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu new String[] {sse.mItemId, mMailboxIdAsString}); 1113d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 1114d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 11154f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11164f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 111748af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 111848af7392c82262d17700e3fbdccf3a582809d449Marc Blank 111948af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 1120a1e1f139046dfb28ea69c46d392f70764ad6f822Andrew Stadler public void commit() { 1121527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank commitImpl(0); 1122527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank } 1123527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank 1124527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank public void commitImpl(int tryCount) { 1125ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Use a batch operation to handle the changes 1126ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 11274f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1128527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank // Maximum size of message text per fetch 1129527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank int numFetched = fetchedEmails.size(); 1130527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank int maxPerFetch = 0; 1131527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank if (numFetched > 0 && tryCount > 0) { 1132527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank // Educated guess that 450000 chars (900k) is ok; 600k is a killer 1133527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank // Remember that when fetching, we're not getting any other data 1134527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank // We'll keep trying, reducing the maximum each time 1135527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank // Realistically, this will rarely exceed 1, and probably never 2 1136527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank maxPerFetch = 450000 / numFetched / tryCount; 1137527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank } 11384f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (Message msg: fetchedEmails) { 11394f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find the original message's id (by serverId and mailbox) 11404f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = getServerIdCursor(msg.mServerId, EmailContent.ID_PROJECTION); 11414f15001bdfd11c79524b4e44d60041967779e763Marc Blank String id = null; 11424f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 11434f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (c.moveToFirst()) { 11444f15001bdfd11c79524b4e44d60041967779e763Marc Blank id = c.getString(EmailContent.ID_PROJECTION_COLUMN); 1145bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank while (c.moveToNext()) { 1146bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank // This shouldn't happen, but clean up if it does 1147bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank Long dupId = 1148bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank Long.parseLong(c.getString(EmailContent.ID_PROJECTION_COLUMN)); 1149bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank userLog("Delete duplicate with id: " + dupId); 1150bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank deletedEmails.add(dupId); 1151bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank } 11524f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11534f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 11544f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 11554f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11564f15001bdfd11c79524b4e44d60041967779e763Marc Blank 11574f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we find one, we do two things atomically: 1) set the body text for the 11584f15001bdfd11c79524b4e44d60041967779e763Marc Blank // message, and 2) mark the message loaded (i.e. completely loaded) 11594f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (id != null) { 11604f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Fetched body successfully for ", id); 1161c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu final String[] bindArgument = new String[] {id}; 1162527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank if ((maxPerFetch > 0) && (msg.mText.length() > maxPerFetch)) { 1163527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank userLog("Truncating message to " + maxPerFetch); 1164527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank msg.mText = msg.mText.substring(0, maxPerFetch) + "..."; 1165527b5952ef438e010ed5a203f12b58a8a2909338Marc Blank } 11664f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Body.CONTENT_URI) 1167c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu .withSelection(Body.MESSAGE_KEY + "=?", bindArgument) 11684f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withValue(Body.TEXT_CONTENT, msg.mText) 11694f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 11704f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Message.CONTENT_URI) 1171c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu .withSelection(EmailContent.RECORD_ID + "=?", bindArgument) 1172068e073ffcb68e06785929208d6a6761b29030f3Marc Blank .withValue(Message.FLAG_LOADED, Message.FLAG_LOADED_COMPLETE) 11734f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 11744f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11754f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11764f15001bdfd11c79524b4e44d60041967779e763Marc Blank 117777424af660458104b732bdcb718874b17d0cab3aMarc Blank for (Message msg: newEmails) { 117877424af660458104b732bdcb718874b17d0cab3aMarc Blank msg.addSaveOps(ops); 1179ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 11804f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1181ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (Long id : deletedEmails) { 1182ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newDelete( 1183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, id)).build()); 1184e951b589c5134a1154ec3743d79236dee54a6519Marc Blank AttachmentUtilities.deleteAllAttachmentFiles(mContext, mAccount.mId, id); 1185ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 11864f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1187ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!changedEmails.isEmpty()) { 1188ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Server wins in a conflict... 1189ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (ServerChange change : changedEmails) { 1190ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank ContentValues cv = new ContentValues(); 1191ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.read != null) { 1192ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_READ, change.read); 1193ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 1194ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.flag != null) { 1195ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_FAVORITE, change.flag); 1196ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 11979b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank if (change.flags != null) { 11989b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank cv.put(MessageColumns.FLAGS, change.flags); 11999b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } 1200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 1201ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, change.id)) 1202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .withValues(cv) 1203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .build()); 1204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 12068d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank 12078d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // We only want to update the sync key here 12088d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentValues mailboxValues = new ContentValues(); 12098d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank mailboxValues.put(Mailbox.SYNC_KEY, mMailbox.mSyncKey); 1210ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 12118d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId)) 12128d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank .withValues(mailboxValues).build()); 1213ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1214c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu try { 1215c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu mContentResolver.applyBatch(EmailContent.AUTHORITY, ops); 1216c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey); 1217c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } catch (TransactionTooLargeException e) { 1218942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy LogUtils.w(TAG, "Transaction failed on fetched message; retrying..."); 1219c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu commitImpl(++tryCount); 1220c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } catch (RemoteException e) { 1221c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu // There is nothing to be done here; fail by returning null 1222c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu } catch (OperationApplicationException e) { 1223c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu // There is nothing to be done here; fail by returning null 1224ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 122577424af660458104b732bdcb718874b17d0cab3aMarc Blank } 122628f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon 122728f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon @Override 122828f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon protected void wipe() { 122928f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon // This file is deprecated, no need to implement this. 123028f3556f92b20b2efb800c1d95c29b04a3bbbb4fMartin Hibdon } 1231ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1232ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1233ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 1234ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String getCollectionName() { 1235ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Email"; 1236ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1237ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 12388480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank private void addCleanupOps(ArrayList<ContentProviderOperation> ops) { 12398480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // If we've sent local deletions, clear out the deleted table 12408480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mDeletedIdList) { 12418480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 12428480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.DELETED_CONTENT_URI, id)).build()); 12438480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 12448480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // And same with the updates 12458480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mUpdatedIdList) { 12468480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 12478480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI, id)).build()); 12488480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 12499c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank } 12509c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank 12519c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank @Override 12529c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank public void cleanup() { 12539c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1254a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank // Delete any moved messages (since we've just synced the mailbox, and no longer need the 1255a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank // placeholder message); this prevents duplicates from appearing in the mailbox. 1256a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank ops.add(ContentProviderOperation.newDelete(Message.CONTENT_URI) 1257c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu .withSelection(WHERE_MAILBOX_KEY_AND_MOVED, 1258c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu new String[] {Long.toString(mMailbox.mId)}).build()); 12599c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank // If we've done deletions/updates, clean up the deleted/updated tables 12608480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank if (!mDeletedIdList.isEmpty() || !mUpdatedIdList.isEmpty()) { 12618480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank addCleanupOps(ops); 12629c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank } 12639c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank try { 12649c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank mContext.getContentResolver() 12659c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank .applyBatch(EmailContent.AUTHORITY, ops); 12669c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank } catch (RemoteException e) { 12679c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank // There is nothing to be done here; fail by returning null 12689c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank } catch (OperationApplicationException e) { 12699c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank // There is nothing to be done here; fail by returning null 12708480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 12718480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 12728480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 1273942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy private static String formatTwo(int num) { 127436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (num < 10) { 127536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return "0" + (char)('0' + num); 127636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else 127736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return Integer.toString(num); 127836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 127936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 128036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank /** 128136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * Create date/time in RFC8601 format. Oddly enough, for calendar date/time, Microsoft uses 128236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * a different format that excludes the punctuation (this is why I'm not putting this in a 128336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * parent class) 128436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank */ 128536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank public String formatDateTime(Calendar calendar) { 128636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank StringBuilder sb = new StringBuilder(); 128736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank //YYYY-MM-DDTHH:MM:SS.MSSZ 128836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(calendar.get(Calendar.YEAR)); 128936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 129036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MONTH) + 1)); 129136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 129236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.DAY_OF_MONTH))); 129336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('T'); 129436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.HOUR_OF_DAY))); 129536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 129636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MINUTE))); 129736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 129836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.SECOND))); 129936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(".000Z"); 130036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return sb.toString(); 130136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 130236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 1303c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /** 1304c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Note that messages in the deleted database preserve the message's unique id; therefore, we 1305c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * can utilize this id to find references to the message. The only reference situation at this 1306c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * point is in the Body table; it is when sending messages via SmartForward and SmartReply 1307c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 1308942b7d73f2f5b3d6c651e39463e615fe6902a910Scott Kennedy private static boolean messageReferenced(ContentResolver cr, long id) { 1309c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // See if this id is referenced in a body 1310c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Body.CONTENT_URI, Body.ID_PROJECTION, WHERE_BODY_SOURCE_MESSAGE_KEY, 1311c9e47f85203da0ea3b6e9a49aa2007d1fc6f0814Yu Ping Hu new String[] {Long.toString(id)}, null); 1312c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank try { 1313c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return c.moveToFirst(); 1314c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } finally { 1315c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank c.close(); 13165acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 1317c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1318c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1319c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /*private*/ /** 1320c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Serialize commands to delete items from the server; as we find items to delete, add their 1321c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * id's to the deletedId's array 1322c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * 1323c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param s the Serializer we're using to create post data 1324c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param deletedIds ids whose deletions are being sent to the server 1325c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param first whether or not this is the first command being sent 1326c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @return true if SYNC_COMMANDS hasn't been sent (false otherwise) 1327c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @throws IOException 1328c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 1329936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 1330c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean sendDeletedItems(Serializer s, ArrayList<Long> deletedIds, boolean first) 1331c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank throws IOException { 1332c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 13335acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank 1334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find any of our deleted items 1335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = cr.query(Message.DELETED_CONTENT_URI, Message.LIST_PROJECTION, 1336ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 1337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We keep track of the list of deleted item id's so that we can remove them from the 1338ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // deleted table after the server receives our command 1339c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.clear(); 1340ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1341ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 13425acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = c.getString(Message.LIST_SERVER_ID_COLUMN); 13435acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 13445acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 13455acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 1346c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Also check if this message is referenced elsewhere 1347c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } else if (messageReferenced(cr, c.getLong(Message.CONTENT_ID_COLUMN))) { 1348c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank userLog("Postponing deletion of referenced message: ", serverId); 1349c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank continue; 13505acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } else if (first) { 13517c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1352ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank first = false; 1353ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1354ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Send the command to delete this message 13555acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 1356c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.add(c.getLong(Message.LIST_ID_COLUMN)); 1357ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1358ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1359ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1360ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1361ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1362c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return first; 1363c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1364c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1365c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank @Override 1366c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank public boolean sendLocalChanges(Serializer s) throws IOException { 1367c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 1368c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1369a261805b03b853cce662b679da3e16120d521b7eMarc Blank if (getSyncKey().equals("0")) { 1370a261805b03b853cce662b679da3e16120d521b7eMarc Blank return false; 1371a261805b03b853cce662b679da3e16120d521b7eMarc Blank } 1372a261805b03b853cce662b679da3e16120d521b7eMarc Blank 1373c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Never upsync from these folders 1374c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (mMailbox.mType == Mailbox.TYPE_DRAFTS || mMailbox.mType == Mailbox.TYPE_OUTBOX) { 1375c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return false; 1376c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1377c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1378c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // This code is split out for unit testing purposes 1379c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean firstCommand = sendDeletedItems(s, mDeletedIdList, true); 1380c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 13814f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (!mFetchRequestList.isEmpty()) { 13824f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Add FETCH commands for messages that need a body (i.e. we didn't find it during 13834f15001bdfd11c79524b4e44d60041967779e763Marc Blank // our earlier sync; this happens only in EAS 2.5 where the body couldn't be found 13844f15001bdfd11c79524b4e44d60041967779e763Marc Blank // after parsing the message's MIME data) 13854f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (firstCommand) { 13864f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_COMMANDS); 13874f15001bdfd11c79524b4e44d60041967779e763Marc Blank firstCommand = false; 13884f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 13894f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (FetchRequest req: mFetchRequestList) { 13904f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_FETCH).data(Tags.SYNC_SERVER_ID, req.serverId).end(); 13914f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 13924f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 13934f15001bdfd11c79524b4e44d60041967779e763Marc Blank 139491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // Find our trash mailbox, since deletions will have been moved there... 139591e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank long trashMailboxId = 13960a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank Mailbox.findMailboxOfType(mContext, mMailbox.mAccountKey, Mailbox.TYPE_TRASH); 139791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 1398ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Do the same now for updated items 1399c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION, 1400ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 140191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 140291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // We keep track of the list of updated item id's as we did above with deleted items 1403ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.clear(); 1404ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 14059c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank ContentValues cv = new ContentValues(); 1406ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 1407ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = c.getLong(Message.LIST_ID_COLUMN); 1408ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Say we've handled this update 1409ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.add(id); 1410ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We have the id of the changed item. But first, we have to find out its current 1411ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // state, since the updated table saves the opriginal state 1412ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor currentCursor = cr.query(ContentUris.withAppendedId(Message.CONTENT_URI, id), 1413ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank UPDATES_PROJECTION, null, null, null); 1414ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1415ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If this item no longer exists (shouldn't be possible), just move along 1416ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!currentCursor.moveToFirst()) { 1417b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo continue; 1418ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 14195acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 14205acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = currentCursor.getString(UPDATES_SERVER_ID_COLUMN); 14215acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 14225acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 14235acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 142491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 142536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean flagChange = false; 142636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean readChange = false; 142736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 14284471a6960d352242cc65bddf7888cc5335840c74Marc Blank long mailbox = currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN); 14294099db352b1881a403813de8e83ab26f31609df3Marc Blank // If the message is now in the trash folder, it has been deleted by the user 14304099db352b1881a403813de8e83ab26f31609df3Marc Blank if (mailbox == trashMailboxId) { 14314099db352b1881a403813de8e83ab26f31609df3Marc Blank if (firstCommand) { 14324099db352b1881a403813de8e83ab26f31609df3Marc Blank s.start(Tags.SYNC_COMMANDS); 14334099db352b1881a403813de8e83ab26f31609df3Marc Blank firstCommand = false; 14344099db352b1881a403813de8e83ab26f31609df3Marc Blank } 14354099db352b1881a403813de8e83ab26f31609df3Marc Blank // Send the command to delete this message 14364099db352b1881a403813de8e83ab26f31609df3Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 14374099db352b1881a403813de8e83ab26f31609df3Marc Blank // Mark the message as moved (so the copy will be deleted if/when the server 14384099db352b1881a403813de8e83ab26f31609df3Marc Blank // version is synced) 14394099db352b1881a403813de8e83ab26f31609df3Marc Blank int flags = c.getInt(Message.LIST_FLAGS_COLUMN); 14404099db352b1881a403813de8e83ab26f31609df3Marc Blank cv.put(MessageColumns.FLAGS, 14414099db352b1881a403813de8e83ab26f31609df3Marc Blank flags | EasSyncService.MESSAGE_FLAG_MOVED_MESSAGE); 14424099db352b1881a403813de8e83ab26f31609df3Marc Blank cr.update(ContentUris.withAppendedId(Message.CONTENT_URI, id), cv, 14434099db352b1881a403813de8e83ab26f31609df3Marc Blank null, null); 14444099db352b1881a403813de8e83ab26f31609df3Marc Blank continue; 14454099db352b1881a403813de8e83ab26f31609df3Marc Blank } else if (mailbox != c.getLong(Message.LIST_MAILBOX_KEY_COLUMN)) { 14464471a6960d352242cc65bddf7888cc5335840c74Marc Blank // The message has moved to another mailbox; add a request for this 14474471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Note: The Sync command doesn't handle moving messages, so we need 14484471a6960d352242cc65bddf7888cc5335840c74Marc Blank // to handle this as a "request" (similar to meeting response and 14494471a6960d352242cc65bddf7888cc5335840c74Marc Blank // attachment load) 14504471a6960d352242cc65bddf7888cc5335840c74Marc Blank mService.addRequest(new MessageMoveRequest(id, mailbox)); 14514471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Regardless of other changes that might be made, we don't want to indicate 14524471a6960d352242cc65bddf7888cc5335840c74Marc Blank // that this message has been updated until the move request has been 14534471a6960d352242cc65bddf7888cc5335840c74Marc Blank // handled (without this, a crash between the flag upsync and the move 14544471a6960d352242cc65bddf7888cc5335840c74Marc Blank // would cause the move to be lost) 14554471a6960d352242cc65bddf7888cc5335840c74Marc Blank mUpdatedIdList.remove(id); 14564471a6960d352242cc65bddf7888cc5335840c74Marc Blank } 145736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 145836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // We can only send flag changes to the server in 12.0 or later 14594471a6960d352242cc65bddf7888cc5335840c74Marc Blank int flag = 0; 1460d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 146136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flag = currentCursor.getInt(UPDATES_FLAG_COLUMN); 146236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != c.getInt(Message.LIST_FAVORITE_COLUMN)) { 146336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flagChange = true; 146436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 146536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 146636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 146791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank int read = currentCursor.getInt(UPDATES_READ_COLUMN); 146876eb7b252fc410f5f5d4e90ad54d4bde837de0aaMarc Blank if (read != c.getInt(Message.LIST_READ_COLUMN)) { 146936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank readChange = true; 147036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 147136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 147236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (!flagChange && !readChange) { 147336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // In this case, we've got nothing to send to the server 1474ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 1475ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 147636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 1477c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (firstCommand) { 14787c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1479c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank firstCommand = false; 1480ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 148136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Send the change to "read" and "favorite" (flagged) 14827c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_CHANGE) 14837c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.SYNC_SERVER_ID, c.getString(Message.LIST_SERVER_ID_COLUMN)) 148436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank .start(Tags.SYNC_APPLICATION_DATA); 148536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (readChange) { 148636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_READ, Integer.toString(read)); 148736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 148836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "Flag" is a relatively complex concept in EAS 12.0 and above. It is not only 148936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // the boolean "favorite" that we think of in Gmail, but it also represents a 149036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // follow up action, which can include a subject, start and due dates, and even 149136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // recurrences. We don't support any of this as yet, but EAS 12.0 and higher 149236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // require that a flag contain a status, a type, and four date fields, two each 149336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // for start date and end (due) date. 149436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flagChange) { 149536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != 0) { 149636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Status 2 = set flag 149736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.start(Tags.EMAIL_FLAG).data(Tags.EMAIL_FLAG_STATUS, "2"); 149836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "FollowUp" is the standard type 149936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_FLAG_TYPE, "FollowUp"); 150036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank long now = System.currentTimeMillis(); 150136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank Calendar calendar = 150236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT")); 150336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now); 150436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Flags are required to have a start date and end date (duplicated) 150536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // First, we'll set the current date/time in GMT as the start time 150636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank String utc = formatDateTime(calendar); 150736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_START_DATE, utc).data(Tags.TASK_UTC_START_DATE, utc); 150836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // And then we'll use one week from today for completion date 150936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now + 1*WEEKS); 151036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank utc = formatDateTime(calendar); 151136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_DUE_DATE, utc).data(Tags.TASK_UTC_DUE_DATE, utc); 151236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end(); 151336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else { 151436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.tag(Tags.EMAIL_FLAG); 151536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 151636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 151736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE 1518ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1519ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank currentCursor.close(); 1520ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1521ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1522ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1523ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1524ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1525ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1526c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (!firstCommand) { 15277c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.end(); // SYNC_COMMANDS 1528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1529ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return false; 1530ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1531ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank} 1532