EmailSyncAdapter.java revision e6c2456aa6c00ef78c6d1d1621511d7ef8507f83
1ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/* 2ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Copyright (C) 2008-2009 Marc Blank 3ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed to The Android Open Source Project. 4ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 5ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Licensed under the Apache License, Version 2.0 (the "License"); 6ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * you may not use this file except in compliance with the License. 7ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * You may obtain a copy of the License at 8ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 9ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * http://www.apache.org/licenses/LICENSE-2.0 10ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 11ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Unless required by applicable law or agreed to in writing, software 12ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * distributed under the License is distributed on an "AS IS" BASIS, 13ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * See the License for the specific language governing permissions and 15ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * limitations under the License. 16ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 17ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 18ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpackage com.android.exchange.adapter; 19ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 209b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentProviderOperation; 219b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentResolver; 229b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentUris; 239b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.ContentValues; 249b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.content.OperationApplicationException; 259b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.database.Cursor; 260dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blankimport android.net.Uri; 279b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.os.RemoteException; 28e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport android.provider.CalendarContract.Events; 29e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport android.text.Html; 30e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport android.text.SpannedString; 31ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blankimport android.text.TextUtils; 32b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugoimport android.util.Base64; 339b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.util.Log; 349b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blankimport android.webkit.MimeTypeMap; 359b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank 36c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.internet.MimeMessage; 37c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.internet.MimeUtility; 38c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.Address; 39c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.MeetingInfo; 40c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.MessagingException; 41c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.PackedString; 42c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.mail.Part; 430dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blankimport com.android.emailcommon.provider.Account; 44c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent; 450dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blankimport com.android.emailcommon.provider.EmailContent.AccountColumns; 46c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Attachment; 47c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Body; 480565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport com.android.emailcommon.provider.EmailContent.MailboxColumns; 49c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.Message; 50c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.MessageColumns; 51c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.provider.EmailContent.SyncColumns; 524d8774462ace9a45154b2df418b9f2fe7a9c685dBen Komaloimport com.android.emailcommon.provider.Mailbox; 53d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blankimport com.android.emailcommon.provider.Policy; 5434d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blankimport com.android.emailcommon.provider.ProviderUnavailableException; 55e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.service.SyncWindow; 56e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.utility.AttachmentUtilities; 57e951b589c5134a1154ec3743d79236dee54a6519Marc Blankimport com.android.emailcommon.utility.ConversionUtilities; 58e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blankimport com.android.emailcommon.utility.TextUtilities; 59c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blankimport com.android.emailcommon.utility.Utility; 6077186bb1a174432ef272584374942d8b9228e39cMarc Blankimport com.android.exchange.CommandStatusException; 6100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.Eas; 62498c903e02ef1b150d6dbd3a01d35839026db264Ben Komaloimport com.android.exchange.EasResponse; 6300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.EasSyncService; 644471a6960d352242cc65bddf7888cc5335840c74Marc Blankimport com.android.exchange.MessageMoveRequest; 650565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport com.android.exchange.R; 66c10a3beef4f048292e6a4ceb31527c5123801517Marc Blankimport com.android.exchange.utility.CalendarUtilities; 67277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blankimport com.google.common.annotations.VisibleForTesting; 68ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 690565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport org.apache.http.HttpStatus; 700565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blankimport org.apache.http.entity.ByteArrayEntity; 710565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 724f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport java.io.ByteArrayInputStream; 7300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException; 7400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream; 7500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList; 7636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blankimport java.util.Calendar; 7700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.GregorianCalendar; 7800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.TimeZone; 79ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 80ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/** 81ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Sync adapter for EAS email 82ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 83ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 847c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankpublic class EmailSyncAdapter extends AbstractSyncAdapter { 8500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 8669c1a51ac81fe2c649e5f2069dc75565d868db98Ben Komalo private static final String TAG = "EmailSyncAdapter"; 8769c1a51ac81fe2c649e5f2069dc75565d868db98Ben Komalo 8891e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_READ_COLUMN = 0; 8991e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_MAILBOX_KEY_COLUMN = 1; 9091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_SERVER_ID_COLUMN = 2; 9136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private static final int UPDATES_FLAG_COLUMN = 3; 9291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final String[] UPDATES_PROJECTION = 9336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank {MessageColumns.FLAG_READ, MessageColumns.MAILBOX_KEY, SyncColumns.SERVER_ID, 9436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank MessageColumns.FLAG_FAVORITE}; 9549c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank 9649c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_ID_COLUMN = 0; 9749c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_SUBJECT_COLUMN = 1; 9818e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank private static final String[] MESSAGE_ID_SUBJECT_PROJECTION = 9918e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank new String[] { Message.RECORD_ID, MessageColumns.SUBJECT }; 10018e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank 101c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private static final String WHERE_BODY_SOURCE_MESSAGE_KEY = Body.SOURCE_MESSAGE_KEY + "=?"; 102a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank private static final String WHERE_MAILBOX_KEY_AND_MOVED = 103a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank MessageColumns.MAILBOX_KEY + "=? AND (" + MessageColumns.FLAGS + "&" + 104a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank EasSyncService.MESSAGE_FLAG_MOVED_MESSAGE + ")!=0"; 1054f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String[] FETCH_REQUEST_PROJECTION = 1064f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {EmailContent.RECORD_ID, SyncColumns.SERVER_ID}; 1074f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_RECORD_ID = 0; 1084f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_SERVER_ID = 1; 1094f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1104f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String EMAIL_WINDOW_SIZE = "5"; 11191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 1129b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1139b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_REPLY = 1; 1149b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1159b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_REPLY_ALL = 2; 1169b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank @VisibleForTesting 1179b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank static final int LAST_VERB_FORWARD = 3; 118422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank 119498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final String[] mBindArguments = new String[2]; 120498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final String[] mBindArgument = new String[1]; 121ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 122936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 123936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy ArrayList<Long> mDeletedIdList = new ArrayList<Long>(); 124936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 125936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy ArrayList<Long> mUpdatedIdList = new ArrayList<Long>(); 126498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<FetchRequest> mFetchRequestList = new ArrayList<FetchRequest>(); 1274f15001bdfd11c79524b4e44d60041967779e763Marc Blank private boolean mFetchNeeded = false; 128ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1298efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Holds the parser's value for isLooping() 130936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private boolean mIsLooping = false; 1318efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 132d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank // The policy (if any) for this adapter's Account 133d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank private final Policy mPolicy; 134d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank 1356f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank public EmailSyncAdapter(EasSyncService service) { 1366f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank super(service); 137d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank // If we've got an account with a policy, cache it now 138d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank if (mAccount.mPolicyKey != 0) { 139d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank mPolicy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 140d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } else { 141d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank mPolicy = null; 142d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 1436f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank } 1446f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank 1456f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank @Override 1466f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank public void wipe() { 1476f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.CONTENT_URI, 1486f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 1496f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.DELETED_CONTENT_URI, 1506f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 1516f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank mContentResolver.delete(Message.UPDATED_CONTENT_URI, 1526f898deac953e5838fa28f47a16e0fb92bbc81ebMarc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 153057faf66b737bbc7df2eaf77ee7a63827785e1b9Marc Blank mService.clearRequests(); 154a261805b03b853cce662b679da3e16120d521b7eMarc Blank mFetchRequestList.clear(); 155134346f5b886e6b53074238546653cdc76bbe868Marc Blank // Delete attachments... 156c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank AttachmentUtilities.deleteAllMailboxAttachmentFiles(mContext, mAccount.mId, mMailbox.mId); 157ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 158ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1594f15001bdfd11c79524b4e44d60041967779e763Marc Blank private String getEmailFilter() { 1608bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank int syncLookback = mMailbox.mSyncLookback; 161b43c40606146babc767475bbabac5820efd4c604Marc Blank if (syncLookback == SyncWindow.SYNC_WINDOW_UNKNOWN 162b43c40606146babc767475bbabac5820efd4c604Marc Blank || mMailbox.mType == Mailbox.TYPE_INBOX) { 1638bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank syncLookback = mAccount.mSyncLookback; 1648bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank } 1658bad9caf330ff72a73d9b7b98ecba7ce5f57ffc9Marc Blank switch (syncLookback) { 1660565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank case SyncWindow.SYNC_WINDOW_AUTO: 1670565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return Eas.FILTER_AUTO; 168e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_DAY: 1694f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_DAY; 170e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_3_DAYS: 1714f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_3_DAYS; 172e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_WEEK: 1734f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 174e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_2_WEEKS: 1754f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_2_WEEKS; 176e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_1_MONTH: 1774f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_MONTH; 178e951b589c5134a1154ec3743d79236dee54a6519Marc Blank case SyncWindow.SYNC_WINDOW_ALL: 1794f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_ALL; 1804f15001bdfd11c79524b4e44d60041967779e763Marc Blank default: 1814f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 1824f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1834f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1844f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1854f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 1864f15001bdfd11c79524b4e44d60041967779e763Marc Blank * Holder for fetch request information (record id and server id) 1874f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 188936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private static class FetchRequest { 189936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @SuppressWarnings("unused") 1904f15001bdfd11c79524b4e44d60041967779e763Marc Blank final long messageId; 1914f15001bdfd11c79524b4e44d60041967779e763Marc Blank final String serverId; 1924f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1934f15001bdfd11c79524b4e44d60041967779e763Marc Blank FetchRequest(long _messageId, String _serverId) { 1944f15001bdfd11c79524b4e44d60041967779e763Marc Blank messageId = _messageId; 1954f15001bdfd11c79524b4e44d60041967779e763Marc Blank serverId = _serverId; 1964f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1974f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1984f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1994f15001bdfd11c79524b4e44d60041967779e763Marc Blank @Override 200e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank public void sendSyncOptions(Double protocolVersion, Serializer s, boolean initialSync) 2014f15001bdfd11c79524b4e44d60041967779e763Marc Blank throws IOException { 202e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (initialSync) return; 2034f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.clear(); 2044f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find partially loaded messages; this should typically be a rare occurrence 2054f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = mContext.getContentResolver().query(Message.CONTENT_URI, 2064f15001bdfd11c79524b4e44d60041967779e763Marc Blank FETCH_REQUEST_PROJECTION, 2074f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.FLAG_LOADED + "=" + Message.FLAG_LOADED_PARTIAL + " AND " + 2084f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.MAILBOX_KEY + "=?", 2094f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {Long.toString(mMailbox.mId)}, null); 2104f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 2114f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Put all of these messages into a list; we'll need both id and server id 2124f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (c.moveToNext()) { 2134f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.add(new FetchRequest(c.getLong(FETCH_REQUEST_RECORD_ID), 2144f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.getString(FETCH_REQUEST_SERVER_ID))); 2154f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2164f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 2174f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 2184f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2194f15001bdfd11c79524b4e44d60041967779e763Marc Blank 2204f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The "empty" case is typical; we send a request for changes, and also specify a sync 2214f15001bdfd11c79524b4e44d60041967779e763Marc Blank // window, body preference type (HTML for EAS 12.0 and later; MIME for EAS 2.5), and 2224f15001bdfd11c79524b4e44d60041967779e763Marc Blank // truncation 2234f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If there are fetch requests, we only want the fetches (i.e. no changes from the server) 2244f15001bdfd11c79524b4e44d60041967779e763Marc Blank // so we turn MIME support off. Note that we are always using EAS 2.5 if there are fetch 2254f15001bdfd11c79524b4e44d60041967779e763Marc Blank // requests 2264f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchRequestList.isEmpty()) { 2271d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank // Permanently delete if in trash mailbox 2281d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank // In Exchange 2003, deletes-as-moves tag = true; no tag = false 2291d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank // In Exchange 2007 and up, deletes-as-moves tag is "0" (false) or "1" (true) 2301d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank boolean isTrashMailbox = mMailbox.mType == Mailbox.TYPE_TRASH; 2311d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank if (protocolVersion < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 2321d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank if (!isTrashMailbox) { 2331d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank s.tag(Tags.SYNC_DELETES_AS_MOVES); 2341d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank } 2351d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank } else { 2361d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank s.data(Tags.SYNC_DELETES_AS_MOVES, isTrashMailbox ? "0" : "1"); 2371d4f429f583dd6ba18d0894b2b1d02afb968c96fMarc Blank } 2384f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.tag(Tags.SYNC_GET_CHANGES); 2394f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_WINDOW_SIZE, EMAIL_WINDOW_SIZE); 2404f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 2414f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the lookback appropriately (EAS calls this a "filter") 2420d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank String filter = getEmailFilter(); 2430d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank // We shouldn't get FILTER_AUTO here, but if we do, make it something legal... 2440d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank if (filter.equals(Eas.FILTER_AUTO)) { 2450d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank filter = Eas.FILTER_3_DAYS; 2460d8fe734daad069d8652688d62a2753b0457d1eaMarc Blank } 247aead58d49204e28a78523c19bd86ad14a0599318Marc Blank s.data(Tags.SYNC_FILTER_TYPE, filter); 2484f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the truncation amount for all classes 2494f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (protocolVersion >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 2504f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.BASE_BODY_PREFERENCE); 2514f15001bdfd11c79524b4e44d60041967779e763Marc Blank // HTML for email 2524f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML); 2534f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE); 2544f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2554f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 2564f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Use MIME data for EAS 2.5 2574f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_MIME); 2584f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 2594f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2604f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2614f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 2624f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 2634f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Ask for plain text, rather than MIME data. This guarantees that we'll get a usable 2644f15001bdfd11c79524b4e44d60041967779e763Marc Blank // text body 2654f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_TEXT); 2664f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 2674f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 2684f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2694f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2704f15001bdfd11c79524b4e44d60041967779e763Marc Blank 271ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 27277186bb1a174432ef272584374942d8b9228e39cMarc Blank public boolean parse(InputStream is) throws IOException, CommandStatusException { 27348af7392c82262d17700e3fbdccf3a582809d449Marc Blank EasEmailSyncParser p = new EasEmailSyncParser(is, this); 2744f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = false; 2758efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank boolean res = p.parse(); 2768efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Hold on to the parser's value for isLooping() to pass back to the service 2778efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank mIsLooping = p.isLooping(); 2784f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we've need a body fetch, or we've just finished one, return true in order to continue 2794f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchNeeded || !mFetchRequestList.isEmpty()) { 2804f15001bdfd11c79524b4e44d60041967779e763Marc Blank return true; 2814f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2820565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 2830565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Don't check for "auto" on the initial sync 2840565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (!("0".equals(mMailbox.mSyncKey))) { 2850565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // We've completed the first successful sync 2860565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (getEmailFilter().equals(Eas.FILTER_AUTO)) { 2870565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank getAutomaticLookback(); 2880565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 2890565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 2900565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 2918efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return res; 2928efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank } 2938efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 2940565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private void getAutomaticLookback() throws IOException { 2950565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If we're using an auto lookback, check how many items in the past week 2960565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // TODO Make the literal ints below constants once we twiddle them a bit 2970565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank int items = getEstimate(Eas.FILTER_1_WEEK); 2980565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank int lookback; 2990565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (items > 1050) { 3000565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Over 150/day, just use one day (smallest) 3010565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_DAY; 3020565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 350 || (items == -1)) { 3030565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 50-150/day, use 3 days (150 to 450 messages synced) 3040565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_3_DAYS; 3050565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 150) { 3060565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 20-50/day, use 1 week (140 to 350 messages synced) 3070565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_WEEK; 3080565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items > 75) { 3090565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // 10-25/day, use 1 week (140 to 350 messages synced) 3100565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_2_WEEKS; 3110565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (items < 5) { 3120565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If there are only a couple, see if it makes sense to get everything 3130565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank items = getEstimate(Eas.FILTER_ALL); 3140565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (items >= 0 && items < 100) { 3150565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_ALL; 3160565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3170565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_MONTH; 3180565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3190565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3200565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank lookback = SyncWindow.SYNC_WINDOW_1_MONTH; 3210565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3220dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank 32330726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank // Limit lookback to policy limit 32430726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank if (mAccount.mPolicyKey > 0) { 32530726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank Policy policy = Policy.restorePolicyWithId(mContext, mAccount.mPolicyKey); 32630726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank if (policy != null) { 32730726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank int maxLookback = policy.mMaxEmailLookback; 32830726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank if (maxLookback != 0 && (lookback > policy.mMaxEmailLookback)) { 32930726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank lookback = policy.mMaxEmailLookback; 33030726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank } 33130726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank } 33230726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank } 33330726eb1240f82e48182a26de78e69c4763f6d2eMarc Blank 3340565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Store the new lookback and persist it 3350dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank // TODO Code similar to this is used elsewhere (e.g. MailboxSettings); try to clean this up 3360565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank ContentValues cv = new ContentValues(); 3370dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank Uri uri; 3380dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank if (mMailbox.mType == Mailbox.TYPE_INBOX) { 3390dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank mAccount.mSyncLookback = lookback; 3400dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank cv.put(AccountColumns.SYNC_LOOKBACK, lookback); 3410dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank uri = ContentUris.withAppendedId(Account.CONTENT_URI, mAccount.mId); 3420dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank } else { 3430dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank mMailbox.mSyncLookback = lookback; 3440dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank cv.put(MailboxColumns.SYNC_LOOKBACK, lookback); 3450dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank uri = ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId); 3460dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank } 3470dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank mContentResolver.update(uri, cv, null, null); 3480dfbd9efda459c7eab716a8eca5f908973bc585fMarc Blank 3490565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank CharSequence[] windowEntries = mContext.getResources().getTextArray( 3500565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank R.array.account_settings_mail_window_entries); 35169c1a51ac81fe2c649e5f2069dc75565d868db98Ben Komalo Log.d(TAG, "Auto lookback: " + windowEntries[lookback]); 3520565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3530565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 354936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy private static class GetItemEstimateParser extends Parser { 3550565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private static final String TAG = "GetItemEstimateParser"; 3560565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private int mEstimate = -1; 3570565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3580565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public GetItemEstimateParser(InputStream in) throws IOException { 3590565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank super(in); 3600565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3610565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 362936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @Override 3630565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public boolean parse() throws IOException { 3640565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // Loop here through the remaining xml 3650565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(START_DOCUMENT) != END_DOCUMENT) { 3660565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_GET_ITEM_ESTIMATE) { 3670565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseGetItemEstimate(); 3680565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3690565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3700565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3710565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3720565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return true; 3730565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3740565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3750565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseGetItemEstimate() throws IOException { 3760565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_GET_ITEM_ESTIMATE) != END) { 3770565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_RESPONSE) { 3780565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseResponse(); 3790565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3800565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3810565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3820565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3830565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3840565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3850565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseResponse() throws IOException { 3860565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_RESPONSE) != END) { 3870565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_STATUS) { 3880565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Log.d(TAG, "GIE status: " + getValue()); 3890565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_COLLECTION) { 3900565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank parseCollection(); 3910565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 3920565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 3930565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3940565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3950565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 3960565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 3970565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank public void parseCollection() throws IOException { 3980565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank while (nextTag(Tags.GIE_COLLECTION) != END) { 3990565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank if (tag == Tags.GIE_CLASS) { 4000565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Log.d(TAG, "GIE class: " + getValue()); 4010565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_COLLECTION_ID) { 4020565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Log.d(TAG, "GIE collectionId: " + getValue()); 4030565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else if (tag == Tags.GIE_ESTIMATE) { 4040565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank mEstimate = getValueInt(); 4050565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Log.d(TAG, "GIE estimate: " + mEstimate); 4060565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } else { 4070565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank skipTag(); 4080565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4090565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4100565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4110565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4120565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 4130565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank /** 4140565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * Return the estimated number of items to be synced in the current mailbox, based on the 4150565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * passed in filter argument 4160565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @param filter an EAS "window" filter 4170565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @return the estimated number of items to be synced, or -1 if unknown 4180565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank * @throws IOException 4190565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank */ 4200565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank private int getEstimate(String filter) throws IOException { 4210565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank Serializer s = new Serializer(); 422277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex10 = mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2010_DOUBLE; 423277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex03 = mService.mProtocolVersionDouble < Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE; 424277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank boolean ex07 = !ex10 && !ex03; 4250565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 4260565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank String className = getCollectionName(); 4270565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank String syncKey = getSyncKey(); 4280565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank userLog("gie, sending ", className, " syncKey: ", syncKey); 429277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank 4300565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.start(Tags.GIE_GET_ITEM_ESTIMATE).start(Tags.GIE_COLLECTIONS); 4310565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.start(Tags.GIE_COLLECTION); 432277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank if (ex07) { 433277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2007 likes collection id first 434277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 435277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_FILTER_TYPE, filter); 436277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 437277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank } else if (ex03) { 438277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2003 needs the "class" element 4390565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank s.data(Tags.GIE_CLASS, className); 440277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 441277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 442277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_FILTER_TYPE, filter); 443277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank } else { 444277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank // Exchange 2010 requires the filter inside an OPTIONS container and sync key first 445277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.SYNC_SYNC_KEY, syncKey); 446277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.data(Tags.GIE_COLLECTION_ID, mMailbox.mServerId); 447277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.start(Tags.SYNC_OPTIONS).data(Tags.SYNC_FILTER_TYPE, filter).end(); 4480565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 449277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank s.end().end().end().done(); // GIE_COLLECTION, GIE_COLLECTIONS, GIE_GET_ITEM_ESTIMATE 450277be74f5d0abcc3bb23cd13fae9d628b131e2bfMarc Blank 4510565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank EasResponse resp = mService.sendHttpClientPost("GetItemEstimate", 4520565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank new ByteArrayEntity(s.toByteArray()), EasSyncService.COMMAND_TIMEOUT); 4536760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank try { 4546760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank int code = resp.getStatus(); 4556760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank if (code == HttpStatus.SC_OK) { 4566760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank if (!resp.isEmpty()) { 4576760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank InputStream is = resp.getInputStream(); 4586760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank GetItemEstimateParser gieParser = new GetItemEstimateParser(is); 4596760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank gieParser.parse(); 4606760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank // Return the estimated number of items 4616760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank return gieParser.mEstimate; 4626760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } 4630565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4646760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank } finally { 4656760c30a8218c4ab1d38774ed0d765384ca1189bMarc Blank resp.close(); 4660565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4670565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank // If we can't get an estimate, indicate this... 4680565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank return -1; 4690565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank } 4700565fd4f943aa3e5be5e001fb16d2f3d69159de6Marc Blank 4718efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank /** 4728efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank * Return the value of isLooping() as returned from the parser 4738efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank */ 4748efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank @Override 4758efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank public boolean isLooping() { 4768efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return mIsLooping; 477ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 478147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 479aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank @Override 480aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank public boolean isSyncable() { 481aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank return true; 482aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank } 483aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank 484368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank public class EasEmailSyncParser extends AbstractSyncParser { 485ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 486147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY = 487ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?"; 488ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 489498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final String mMailboxIdAsString; 490ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 491498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Message> newEmails = new ArrayList<Message>(); 492498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Message> fetchedEmails = new ArrayList<Message>(); 493498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<Long> deletedEmails = new ArrayList<Long>(); 494498c903e02ef1b150d6dbd3a01d35839026db264Ben Komalo private final ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>(); 49548af7392c82262d17700e3fbdccf3a582809d449Marc Blank 49648af7392c82262d17700e3fbdccf3a582809d449Marc Blank public EasEmailSyncParser(InputStream in, EmailSyncAdapter adapter) throws IOException { 49748af7392c82262d17700e3fbdccf3a582809d449Marc Blank super(in, adapter); 498ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mMailboxIdAsString = Long.toString(mMailbox.mId); 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); 50426d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank } 50526d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank 50626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank public void addData (Message msg, int endingTag) throws IOException { 507ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<Attachment> atts = new ArrayList<Attachment>(); 5084f15001bdfd11c79524b4e44d60041967779e763Marc Blank boolean truncated = false; 509ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 51026d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank while (nextTag(endingTag) != END) { 511ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 5127c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATTACHMENTS: 5135ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up 514d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank attachmentsParser(atts, msg); 515ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5167c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_TO: 51767698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mTo = Address.pack(Address.parse(getValue())); 518ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5197c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FROM: 520705a309bd78efd77469ac90a57849619de3317e3Mihai Preda Address[] froms = Address.parse(getValue()); 521705a309bd78efd77469ac90a57849619de3317e3Mihai Preda if (froms != null && froms.length > 0) { 5224f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mDisplayName = froms[0].toFriendly(); 523ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 524705a309bd78efd77469ac90a57849619de3317e3Mihai Preda msg.mFrom = Address.pack(froms); 525ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5267c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_CC: 52767698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mCc = Address.pack(Address.parse(getValue())); 528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5297c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_REPLY_TO: 53067698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mReplyTo = Address.pack(Address.parse(getValue())); 531ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5327c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DATE_RECEIVED: 5338e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue()); 534ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5357c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_SUBJECT: 536ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mSubject = getValue(); 537ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5387c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 539ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagRead = getValueInt() == 1; 540ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5417c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_BODY: 54200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyParser(msg); 54300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 5447c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 545ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank msg.mFlagFavorite = flagParser(); 546ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 5474f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_TRUNCATED: 5484f15001bdfd11c79524b4e44d60041967779e763Marc Blank truncated = getValueInt() == 1; 5494f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 5504f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_DATA: 5514f15001bdfd11c79524b4e44d60041967779e763Marc Blank // We get MIME data for EAS 2.5. First we parse it, then we take the 5524f15001bdfd11c79524b4e44d60041967779e763Marc Blank // html and/or plain text data and store it in the message 553068e073ffcb68e06785929208d6a6761b29030f3Marc Blank if (truncated) { 554068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // If the MIME data is truncated, don't bother parsing it, because 555068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // it will take time and throw an exception anyway when EOF is reached 556068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // In this case, we will load the body separately by tagging the message 557068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // "partially loaded". 5583fb34bbdabba3ef5bc5742aa012fd9245944165fMarc Blank // Get the data (and ignore it) 5593fb34bbdabba3ef5bc5742aa012fd9245944165fMarc Blank getValue(); 5604f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Partially loaded: ", msg.mServerId); 5614f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL; 5624f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = true; 563068e073ffcb68e06785929208d6a6761b29030f3Marc Blank } else { 564068e073ffcb68e06785929208d6a6761b29030f3Marc Blank mimeBodyParser(msg, getValue()); 5654f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 5664f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 5677c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_BODY: 56867698e240187c902bed123bf18d342ff25ec75c7Marc Blank String text = getValue(); 56967698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mText = text; 570ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5717531be7774769c84b499b1de5dc46da3a9468316Marc Blank case Tags.EMAIL_MESSAGE_CLASS: 5727531be7774769c84b499b1de5dc46da3a9468316Marc Blank String messageClass = getValue(); 5737531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (messageClass.equals("IPM.Schedule.Meeting.Request")) { 574888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_INVITE; 5757531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) { 576888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_CANCEL; 5777531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 5787531be7774769c84b499b1de5dc46da3a9468316Marc Blank break; 579c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_MEETING_REQUEST: 580c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank meetingRequestParser(msg); 581c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 582e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank case Tags.EMAIL_THREAD_TOPIC: 583e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank msg.mThreadTopic = getValue(); 584e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank break; 58577186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.RIGHTS_LICENSE: 58677186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 58777186bb1a174432ef272584374942d8b9228e39cMarc Blank break; 58877186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.EMAIL2_CONVERSATION_ID: 589b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo msg.mServerConversationId = 590b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo Base64.encodeToString(getValueBytes(), Base64.URL_SAFE); 591b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo break; 59277186bb1a174432ef272584374942d8b9228e39cMarc Blank case Tags.EMAIL2_CONVERSATION_INDEX: 593b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo // Ignore this byte array since we're not constructing a tree. 59477186bb1a174432ef272584374942d8b9228e39cMarc Blank getValueBytes(); 59577186bb1a174432ef272584374942d8b9228e39cMarc Blank break; 596422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank case Tags.EMAIL2_LAST_VERB_EXECUTED: 597422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank int val = getValueInt(); 598422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { 599422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank // We aren't required to distinguish between reply and reply all here 600422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank msg.mFlags |= Message.FLAG_REPLIED_TO; 601422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank } else if (val == LAST_VERB_FORWARD) { 602422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank msg.mFlags |= Message.FLAG_FORWARDED; 603422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank } 604422a3b5f8b8b3efbecaec9bc53860db546bfbe34Marc Blank break; 605ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 606ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 607ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 608ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 609ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 610ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (atts.size() > 0) { 611ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAttachments = atts; 612ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 613e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 614e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if ((msg.mFlags & Message.FLAG_INCOMING_MEETING_MASK) != 0) { 615e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String text = TextUtilities.makeSnippetFromHtmlText( 616e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank msg.mText != null ? msg.mText : msg.mHtml); 617e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (TextUtils.isEmpty(text)) { 618e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank // Create text for this invitation 619e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String meetingInfo = msg.mMeetingInfo; 620e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (!TextUtils.isEmpty(meetingInfo)) { 621e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank PackedString ps = new PackedString(meetingInfo); 622e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank ContentValues values = new ContentValues(); 623e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank putFromMeeting(ps, MeetingInfo.MEETING_LOCATION, values, 624e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Events.EVENT_LOCATION); 625e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String dtstart = ps.get(MeetingInfo.MEETING_DTSTART); 626e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (!TextUtils.isEmpty(dtstart)) { 627e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank long startTime = Utility.parseEmailDateTimeToMillis(dtstart); 628e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank values.put(Events.DTSTART, startTime); 629e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 630e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank putFromMeeting(ps, MeetingInfo.MEETING_ALL_DAY, values, 631e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank Events.ALL_DAY); 632e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank msg.mText = CalendarUtilities.buildMessageTextFromEntityValues( 633e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank mContext, values, null); 634e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank msg.mHtml = Html.toHtml(new SpannedString(msg.mText)); 635e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 636e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 637e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 638e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 639e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank 640e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank private void putFromMeeting(PackedString ps, String field, ContentValues values, 641e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String column) { 642e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank String val = ps.get(field); 643e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (!TextUtils.isEmpty(val)) { 644e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank values.put(column, val); 645e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 646ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 64767698e240187c902bed123bf18d342ff25ec75c7Marc Blank 6485c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank /** 6495c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * Set up the meetingInfo field in the message with various pieces of information gleaned 6505c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * from MeetingRequest tags. This information will be used later to generate an appropriate 6515c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * reply email if the user chooses to respond 6525c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @param msg the Message being built 6535c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @throws IOException 6545c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank */ 655c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void meetingRequestParser(Message msg) throws IOException { 6565c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank PackedString.Builder packedString = new PackedString.Builder(); 657c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_MEETING_REQUEST) != END) { 658c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 6595c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_DTSTAMP: 6605c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTAMP, getValue()); 6615c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 662c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_START_TIME: 6635c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTART, getValue()); 6645c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 6655c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_END_TIME: 6665c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTEND, getValue()); 6675c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 6685c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_ORGANIZER: 6695c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_ORGANIZER_EMAIL, getValue()); 6705c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 6715c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_LOCATION: 6725c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_LOCATION, getValue()); 6735c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 6745c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_GLOBAL_OBJID: 675b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank packedString.put(MeetingInfo.MEETING_UID, 676b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank CalendarUtilities.getUidFromGlobalObjId(getValue())); 677c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 678c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_CATEGORIES: 67977186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 680c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 681c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCES: 682c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank recurrencesParser(); 683c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 68439fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank case Tags.EMAIL_RESPONSE_REQUESTED: 68539fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank packedString.put(MeetingInfo.MEETING_RESPONSE_REQUESTED, getValue()); 68639fd7f258e1ca1a8d83dd53aff6da3ebb5007456Marc Blank break; 687e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank case Tags.EMAIL_ALL_DAY_EVENT: 688e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank if (getValueInt() == 1) { 689e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank packedString.put(MeetingInfo.MEETING_ALL_DAY, getValue()); 690e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank } 691e6c2456aa6c00ef78c6d1d1621511d7ef8507f83Marc Blank break; 692c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 693c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 694c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 695c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 6965c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (msg.mSubject != null) { 6975c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_TITLE, msg.mSubject); 6985c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 6995c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank msg.mMeetingInfo = packedString.toString(); 700c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 701c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 702c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void recurrencesParser() throws IOException { 703c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_RECURRENCES) != END) { 704c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 705c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCE: 70677186bb1a174432ef272584374942d8b9228e39cMarc Blank skipParser(tag); 707f69266d6671aa2c55fd04117a36f74dd17f73067Marc Blank break; 708c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 709c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 710c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 711c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 712c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 713c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 714d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank /** 715d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * Parse a message from the server stream. 716d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * @return the parsed Message 717d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank * @throws IOException 718d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank */ 71977186bb1a174432ef272584374942d8b9228e39cMarc Blank private Message addParser() throws IOException, CommandStatusException { 720ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Message msg = new Message(); 721ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAccountKey = mAccount.mId; 722ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mMailboxKey = mMailbox.mId; 723f02459a766ddb1727d191daa0aeb559c8f848668Andrew Stadler msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE; 724247d8026d6be4609c4d7a8b7be8f2f5a0908e511Marc Blank // Default to 1 (success) in case we don't get this tag 725247d8026d6be4609c4d7a8b7be8f2f5a0908e511Marc Blank int status = 1; 726ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 7277c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_ADD) != END) { 728ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 7297c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 730ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mServerId = getValue(); 731ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 732d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank case Tags.SYNC_STATUS: 733d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank status = getValueInt(); 734d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank break; 7357c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_APPLICATION_DATA: 73626d9677a1eb48553241897b63a77bbd33daa9f92Marc Blank addData(msg, tag); 737ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 738ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 739ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 740ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 741ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 742d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // For sync, status 1 = success 743d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank if (status != 1) { 74477186bb1a174432ef272584374942d8b9228e39cMarc Blank throw new CommandStatusException(status, msg.mServerId); 745d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 746d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank return msg; 747ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 748ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 749ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank // For now, we only care about the "active" state 750ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank private Boolean flagParser() throws IOException { 751ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean state = false; 7527c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_FLAG) != END) { 753ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank switch (tag) { 7547c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG_STATUS: 755ce17455fc5abf061e252d495288d0d56404b0b62Marc Blank state = getValueInt() == 2; 756ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 757ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank default: 758ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank skipTag(); 759ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 760ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 761ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank return state; 762ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 763ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank 76400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void bodyParser(Message msg) throws IOException { 76500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String bodyType = Eas.BODY_PREFERENCE_TEXT; 76600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String body = ""; 7677c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_BODY) != END) { 76800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank switch (tag) { 7697c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_TYPE: 77000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyType = getValue(); 77100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 7727c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_DATA: 77300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank body = getValue(); 77400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 77500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank default: 77600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank skipTag(); 77700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 77800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 77900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // We always ask for TEXT or HTML; there's no third option 78000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) { 78100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mHtml = body; 78200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 78300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mText = body; 78400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 78500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 78600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 7874f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 788068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * Parses untruncated MIME data, saving away the text parts 7894f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param msg the message we're building 7904f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param mimeData the MIME data we've received from the server 791068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * @throws IOException 7924f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 793068e073ffcb68e06785929208d6a6761b29030f3Marc Blank private void mimeBodyParser(Message msg, String mimeData) throws IOException { 7944f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 7954f15001bdfd11c79524b4e44d60041967779e763Marc Blank ByteArrayInputStream in = new ByteArrayInputStream(mimeData.getBytes()); 7964f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The constructor parses the message 797068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeMessage mimeMessage = new MimeMessage(in); 798068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // Now process body parts & attachments 799068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> viewables = new ArrayList<Part>(); 800068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // We'll ignore the attachments, as we'll get them directly from EAS 801068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> attachments = new ArrayList<Part>(); 802068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeUtility.collectParts(mimeMessage, viewables, attachments); 803068e073ffcb68e06785929208d6a6761b29030f3Marc Blank Body tempBody = new Body(); 804068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // updateBodyFields fills in the content fields of the Body 805e951b589c5134a1154ec3743d79236dee54a6519Marc Blank ConversionUtilities.updateBodyFields(tempBody, msg, viewables); 806068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // But we need them in the message itself for handling during commit() 807068e073ffcb68e06785929208d6a6761b29030f3Marc Blank msg.mHtml = tempBody.mHtmlContent; 808068e073ffcb68e06785929208d6a6761b29030f3Marc Blank msg.mText = tempBody.mTextContent; 8094f15001bdfd11c79524b4e44d60041967779e763Marc Blank } catch (MessagingException e) { 810068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // This would most likely indicate a broken stream 811068e073ffcb68e06785929208d6a6761b29030f3Marc Blank throw new IOException(e); 8124f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 8134f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 8144f15001bdfd11c79524b4e44d60041967779e763Marc Blank 8155ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank private void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException { 8165ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENTS) != END) { 8175ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank switch (tag) { 8185ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.EMAIL_ATTACHMENT: 8195ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENT: // BASE_ATTACHMENT is used in EAS 12.0 and up 8205ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank attachmentParser(atts, msg); 8215ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank break; 8225ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank default: 8235ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank skipTag(); 8245ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 8255ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 8265ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 8275ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank 82800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException { 829ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String fileName = null; 830ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String length = null; 831d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank String location = null; 832ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank boolean isInline = false; 833ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank String contentId = null; 834ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 8357c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENT) != END) { 836ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 8375ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank // We handle both EAS 2.5 and 12.0+ attachments here 8387c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DISPLAY_NAME: 8395ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_DISPLAY_NAME: 840ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank fileName = getValue(); 841ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 8427c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_NAME: 8435ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_FILE_REFERENCE: 844d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank location = getValue(); 845ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 8467c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_SIZE: 8475ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ESTIMATED_DATA_SIZE: 848ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank length = getValue(); 849ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 850ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank case Tags.BASE_IS_INLINE: 851ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank isInline = getValueInt() == 1; 852ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank break; 853ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank case Tags.BASE_CONTENT_ID: 854ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank contentId = getValue(); 855ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank break; 856ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 857ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 858ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 859ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 860ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 8615ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((fileName != null) && (length != null) && (location != null)) { 862ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Attachment att = new Attachment(); 863ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mEncoding = "base64"; 864ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mSize = Long.parseLong(length); 865ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mFileName = fileName; 866d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank att.mLocation = location; 86700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank att.mMimeType = getMimeTypeFromFileName(fileName); 868057faf66b737bbc7df2eaf77ee7a63827785e1b9Marc Blank att.mAccountKey = mService.mAccount.mId; 869ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank // Save away the contentId, if we've got one (for inline images); note that the 870ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank // EAS docs appear to be wrong about the tags used; inline images come with 871ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank // contentId rather than contentLocation, when sent from Ex03, Ex07, and Ex10 872ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank if (isInline && !TextUtils.isEmpty(contentId)) { 873ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank att.mContentId = contentId; 874ea68a80285e175614a7b36adea6fd25f09e3a887Marc Blank } 875d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank // Check if this attachment can't be downloaded due to an account policy 876d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank if (mPolicy != null) { 877d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank if (mPolicy.mDontAllowAttachments || 878d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank (mPolicy.mMaxAttachmentSize > 0 && 879d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank (att.mSize > mPolicy.mMaxAttachmentSize))) { 880d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank att.mFlags = Attachment.FLAG_POLICY_DISALLOWS_DOWNLOAD; 881d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 882d1d98cba6f4604c5b88b3c53a09b9741f8c87a54Marc Blank } 883ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank atts.add(att); 884ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagAttachment = true; 885ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 886ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 887ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 88800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /** 889936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * Returns an appropriate mimetype for the given file name's extension. If a mimetype 890936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * cannot be determined, {@code application/<<x>>} [where @{code <<x>> is the extension, 891936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy * if it exists or {@code application/octet-stream}]. 89200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * At the moment, this is somewhat lame, since many file types aren't recognized 89300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @param fileName the file name to ponder 89400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 89500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // Note: The MimeTypeMap method currently uses a very limited set of mime types 89600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A bug has been filed against this issue. 89700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank public String getMimeTypeFromFileName(String fileName) { 89800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String mimeType; 89900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank int lastDot = fileName.lastIndexOf('.'); 90000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String extension = null; 9015ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { 90219fd685351de80c62c9bc7f0f05fe96983a8078dMarc Blank extension = fileName.substring(lastDot + 1).toLowerCase(); 90300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 90400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (extension == null) { 90500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A reasonable default for now. 90600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/octet-stream"; 90700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 90800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 90900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (mimeType == null) { 91000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/" + extension; 91100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 91200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 91300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank return mimeType; 91400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 91500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 916ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private Cursor getServerIdCursor(String serverId, String[] projection) { 917c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArguments[0] = serverId; 918c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArguments[1] = mMailboxIdAsString; 91934d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank Cursor c = mContentResolver.query(Message.CONTENT_URI, projection, 920c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank WHERE_SERVER_ID_AND_MAILBOX_KEY, mBindArguments, null); 92134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (c == null) throw new ProviderUnavailableException(); 922bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank if (c.getCount() > 1) { 923bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank userLog("Multiple messages with the same serverId/mailbox: " + serverId); 924bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank } 92534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank return c; 926ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 927ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 928936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 929936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException { 930048d45641b88c87172074aa5f29b3de307bc3712Marc Blank while (nextTag(entryTag) != END) { 931ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 9327c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 933ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = getValue(); 934ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find the message in this mailbox with the given serverId 93518e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank Cursor c = getServerIdCursor(serverId, MESSAGE_ID_SUBJECT_PROJECTION); 936ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 937ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 93849c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank deletes.add(c.getLong(MESSAGE_ID_SUBJECT_ID_COLUMN)); 93918e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank if (Eas.USER_LOG) { 94049c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank userLog("Deleting ", serverId + ", " 94149c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank + c.getString(MESSAGE_ID_SUBJECT_SUBJECT_COLUMN)); 94218e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank } 943ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 944ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 945ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 946ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 947ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 948ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 949ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 950ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 951ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 952ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 953ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 954936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 955ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank class ServerChange { 9569b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final long id; 9579b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Boolean read; 9589b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Boolean flag; 9599b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank final Integer flags; 960ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 9619b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank ServerChange(long _id, Boolean _read, Boolean _flag, Integer _flags) { 962ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = _id; 963ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = _read; 964ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = _flag; 9659b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = _flags; 966ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 967ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 968ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 969936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 970936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy void changeParser(ArrayList<ServerChange> changes) throws IOException { 971ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = null; 972ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldRead = false; 973ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldFlag = false; 9749b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank int flags = 0; 975ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = 0; 9767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_CHANGE) != END) { 977ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 9787c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 979ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank serverId = getValue(); 980ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = getServerIdCursor(serverId, Message.LIST_PROJECTION); 981ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 982ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 9830a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Changing ", serverId); 984ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank oldRead = c.getInt(Message.LIST_READ_COLUMN) == Message.READ; 985ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank oldFlag = c.getInt(Message.LIST_FAVORITE_COLUMN) == 1; 9869b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = c.getInt(Message.LIST_FLAGS_COLUMN); 987ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = c.getLong(Message.LIST_ID_COLUMN); 988ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 989ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 990ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 991ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 992ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 993d82abe7213806e478338af4202b3622f34b5d6feMarc Blank case Tags.SYNC_APPLICATION_DATA: 9949b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank changeApplicationDataParser(changes, oldRead, oldFlag, flags, id); 995d82abe7213806e478338af4202b3622f34b5d6feMarc Blank break; 996d82abe7213806e478338af4202b3622f34b5d6feMarc Blank default: 997d82abe7213806e478338af4202b3622f34b5d6feMarc Blank skipTag(); 998d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 999d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 1000d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 1001d82abe7213806e478338af4202b3622f34b5d6feMarc Blank 1002d82abe7213806e478338af4202b3622f34b5d6feMarc Blank private void changeApplicationDataParser(ArrayList<ServerChange> changes, Boolean oldRead, 10039b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank Boolean oldFlag, int oldFlags, long id) throws IOException { 1004d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean read = null; 1005d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean flag = null; 10069b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank Integer flags = null; 1007d82abe7213806e478338af4202b3622f34b5d6feMarc Blank while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 1008d82abe7213806e478338af4202b3622f34b5d6feMarc Blank switch (tag) { 10097c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 1010ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = getValueInt() == 1; 1011ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 10127c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 1013ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = flagParser(); 1014ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 10159b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank case Tags.EMAIL2_LAST_VERB_EXECUTED: 10169b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank int val = getValueInt(); 10179b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // Clear out the old replied/forward flags and add in the new flag 10189b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags = oldFlags & ~(Message.FLAG_REPLIED_TO | Message.FLAG_FORWARDED); 10199b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank if (val == LAST_VERB_REPLY || val == LAST_VERB_REPLY_ALL) { 10209b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // We aren't required to distinguish between reply and reply all here 10219b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags |= Message.FLAG_REPLIED_TO; 10229b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } else if (val == LAST_VERB_FORWARD) { 10239b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank flags |= Message.FLAG_FORWARDED; 10249b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } 10259b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank break; 1026ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 1027ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 1028ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1029ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 10309b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank // See if there are flag changes re: read, flag (favorite) or replied/forwarded 10315ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if (((read != null) && !oldRead.equals(read)) || 10329b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank ((flag != null) && !oldFlag.equals(flag)) || (flags != null)) { 10339b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank changes.add(new ServerChange(id, read, flag, flags)); 1034ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1035ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1036ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 103700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /* (non-Javadoc) 103800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @see com.android.exchange.adapter.EasContentParser#commandsParser() 103900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 1040147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank @Override 104177186bb1a174432ef272584374942d8b9228e39cMarc Blank public void commandsParser() throws IOException, CommandStatusException { 10427c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_COMMANDS) != END) { 10437c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank if (tag == Tags.SYNC_ADD) { 1044d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank newEmails.add(addParser()); 10450a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 1046048d45641b88c87172074aa5f29b3de307bc3712Marc Blank } else if (tag == Tags.SYNC_DELETE || tag == Tags.SYNC_SOFT_DELETE) { 1047048d45641b88c87172074aa5f29b3de307bc3712Marc Blank deleteParser(deletedEmails, tag); 10480a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 10497c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_CHANGE) { 1050ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank changeParser(changedEmails); 10510a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 1052ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else 1053ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 1054ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 105548af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 105648af7392c82262d17700e3fbdccf3a582809d449Marc Blank 105734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank /** 105834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank * Removed any messages with status 7 (mismatch) from the updatedIdList 105934d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank * @param endTag the tag we end with 106034d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank * @throws IOException 106134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank */ 106234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank public void failedUpdateParser(int endTag) throws IOException { 106334d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank // We get serverId and status in the responses 106434d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank String serverId = null; 106534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank while (nextTag(endTag) != END) { 106634d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (tag == Tags.SYNC_STATUS) { 106734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank int status = getValueInt(); 106834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (status == 7 && serverId != null) { 106934d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank Cursor c = getServerIdCursor(serverId, Message.ID_COLUMN_PROJECTION); 107034d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank try { 107134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank if (c.moveToFirst()) { 107234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank Long id = c.getLong(Message.ID_PROJECTION_COLUMN); 107334d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank mService.userLog("Update of " + serverId + " failed; will retry"); 107434d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank mUpdatedIdList.remove(id); 107534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank mService.mUpsyncFailed = true; 107634d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 107734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } finally { 107834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank c.close(); 107934d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 108034d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 108134d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } else if (tag == Tags.SYNC_SERVER_ID) { 108234d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank serverId = getValue(); 108334d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } else { 108434d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank skipTag(); 108534d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 108634d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 108734d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank } 108834d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank 108948af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 10904f15001bdfd11c79524b4e44d60041967779e763Marc Blank public void responsesParser() throws IOException { 10914f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (nextTag(Tags.SYNC_RESPONSES) != END) { 10924f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (tag == Tags.SYNC_ADD || tag == Tags.SYNC_CHANGE || tag == Tags.SYNC_DELETE) { 109334d9cf7433233c1a61ca04cb5be131b9207c00abMarc Blank failedUpdateParser(tag); 10944f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else if (tag == Tags.SYNC_FETCH) { 1095d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank try { 1096d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank fetchedEmails.add(addParser()); 109777186bb1a174432ef272584374942d8b9228e39cMarc Blank } catch (CommandStatusException sse) { 1098d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank if (sse.mStatus == 8) { 1099d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // 8 = object not found; delete the message from EmailProvider 1100d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // No other status should be seen in a fetch response, except, perhaps, 1101d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank // for some temporary server failure 110277186bb1a174432ef272584374942d8b9228e39cMarc Blank mBindArguments[0] = sse.mItemId; 1103d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank mBindArguments[1] = mMailboxIdAsString; 1104d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank mContentResolver.delete(Message.CONTENT_URI, 1105d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank WHERE_SERVER_ID_AND_MAILBOX_KEY, mBindArguments); 1106d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 1107d62e26b2ce5a09de6a43c1d2d4f4692eb5aac81aMarc Blank } 11084f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11094f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 111048af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 111148af7392c82262d17700e3fbdccf3a582809d449Marc Blank 111248af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 1113a1e1f139046dfb28ea69c46d392f70764ad6f822Andrew Stadler public void commit() { 1114ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Use a batch operation to handle the changes 1115ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 11164f15001bdfd11c79524b4e44d60041967779e763Marc Blank 11174f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (Message msg: fetchedEmails) { 11184f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find the original message's id (by serverId and mailbox) 11194f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = getServerIdCursor(msg.mServerId, EmailContent.ID_PROJECTION); 11204f15001bdfd11c79524b4e44d60041967779e763Marc Blank String id = null; 11214f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 11224f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (c.moveToFirst()) { 11234f15001bdfd11c79524b4e44d60041967779e763Marc Blank id = c.getString(EmailContent.ID_PROJECTION_COLUMN); 1124bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank while (c.moveToNext()) { 1125bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank // This shouldn't happen, but clean up if it does 1126bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank Long dupId = 1127bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank Long.parseLong(c.getString(EmailContent.ID_PROJECTION_COLUMN)); 1128bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank userLog("Delete duplicate with id: " + dupId); 1129bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank deletedEmails.add(dupId); 1130bf28a9d635f5790fbe158f6891ec84f2b0f3cf9aMarc Blank } 11314f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11324f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 11334f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 11344f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11354f15001bdfd11c79524b4e44d60041967779e763Marc Blank 11364f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we find one, we do two things atomically: 1) set the body text for the 11374f15001bdfd11c79524b4e44d60041967779e763Marc Blank // message, and 2) mark the message loaded (i.e. completely loaded) 11384f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (id != null) { 11394f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Fetched body successfully for ", id); 11404f15001bdfd11c79524b4e44d60041967779e763Marc Blank mBindArgument[0] = id; 11414f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Body.CONTENT_URI) 11424f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withSelection(Body.MESSAGE_KEY + "=?", mBindArgument) 11434f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withValue(Body.TEXT_CONTENT, msg.mText) 11444f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 11454f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Message.CONTENT_URI) 11464f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withSelection(EmailContent.RECORD_ID + "=?", mBindArgument) 1147068e073ffcb68e06785929208d6a6761b29030f3Marc Blank .withValue(Message.FLAG_LOADED, Message.FLAG_LOADED_COMPLETE) 11484f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 11494f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11504f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 11514f15001bdfd11c79524b4e44d60041967779e763Marc Blank 115277424af660458104b732bdcb718874b17d0cab3aMarc Blank for (Message msg: newEmails) { 115377424af660458104b732bdcb718874b17d0cab3aMarc Blank msg.addSaveOps(ops); 1154ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 11554f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1156ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (Long id : deletedEmails) { 1157ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newDelete( 1158ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, id)).build()); 1159e951b589c5134a1154ec3743d79236dee54a6519Marc Blank AttachmentUtilities.deleteAllAttachmentFiles(mContext, mAccount.mId, id); 1160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 11614f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!changedEmails.isEmpty()) { 1163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Server wins in a conflict... 1164ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (ServerChange change : changedEmails) { 1165ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank ContentValues cv = new ContentValues(); 1166ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.read != null) { 1167ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_READ, change.read); 1168ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 1169ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.flag != null) { 1170ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_FAVORITE, change.flag); 1171ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 11729b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank if (change.flags != null) { 11739b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank cv.put(MessageColumns.FLAGS, change.flags); 11749b243f9a4581a63c6369d0f5a4f695edf2e90e38Marc Blank } 1175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 1176ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, change.id)) 1177ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .withValues(cv) 1178ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .build()); 1179ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1180ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 11818d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank 11828d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // We only want to update the sync key here 11838d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentValues mailboxValues = new ContentValues(); 11848d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank mailboxValues.put(Mailbox.SYNC_KEY, mMailbox.mSyncKey); 1185ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 11868d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId)) 11878d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank .withValues(mailboxValues).build()); 1188ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 11891b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // No commits if we're stopped 11901b275b9408d5b856e2482fa3951827489e9585ccMarc Blank synchronized (mService.getSynchronizer()) { 11911b275b9408d5b856e2482fa3951827489e9585ccMarc Blank if (mService.isStopped()) return; 11921b275b9408d5b856e2482fa3951827489e9585ccMarc Blank try { 1193c8e4352ea6cfa67f15140512e84af8ccede222d2Marc Blank mContentResolver.applyBatch(EmailContent.AUTHORITY, ops); 11940a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey); 11951b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } catch (RemoteException e) { 11961b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // There is nothing to be done here; fail by returning null 11971b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } catch (OperationApplicationException e) { 11981b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // There is nothing to be done here; fail by returning null 11991b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } 1200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 120177424af660458104b732bdcb718874b17d0cab3aMarc Blank } 1202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 1205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String getCollectionName() { 1206ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Email"; 1207ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1208ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 12098480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank private void addCleanupOps(ArrayList<ContentProviderOperation> ops) { 12108480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // If we've sent local deletions, clear out the deleted table 12118480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mDeletedIdList) { 12128480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 12138480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.DELETED_CONTENT_URI, id)).build()); 12148480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 12158480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // And same with the updates 12168480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mUpdatedIdList) { 12178480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 12188480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI, id)).build()); 12198480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 12209c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank } 12219c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank 12229c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank @Override 12239c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank public void cleanup() { 12249c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 1225a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank // Delete any moved messages (since we've just synced the mailbox, and no longer need the 1226a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank // placeholder message); this prevents duplicates from appearing in the mailbox. 1227a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank mBindArgument[0] = Long.toString(mMailbox.mId); 1228a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank ops.add(ContentProviderOperation.newDelete(Message.CONTENT_URI) 1229a11e0c0d45e4053824c72dc9042d9b76005da4a6Marc Blank .withSelection(WHERE_MAILBOX_KEY_AND_MOVED, mBindArgument).build()); 12309c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank // If we've done deletions/updates, clean up the deleted/updated tables 12318480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank if (!mDeletedIdList.isEmpty() || !mUpdatedIdList.isEmpty()) { 12328480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank addCleanupOps(ops); 12339c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank } 12349c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank try { 12359c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank mContext.getContentResolver() 12369c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank .applyBatch(EmailContent.AUTHORITY, ops); 12379c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank } catch (RemoteException e) { 12389c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank // There is nothing to be done here; fail by returning null 12399c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank } catch (OperationApplicationException e) { 12409c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank // There is nothing to be done here; fail by returning null 12418480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 12428480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 12438480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 124436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private String formatTwo(int num) { 124536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (num < 10) { 124636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return "0" + (char)('0' + num); 124736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else 124836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return Integer.toString(num); 124936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 125036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 125136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank /** 125236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * Create date/time in RFC8601 format. Oddly enough, for calendar date/time, Microsoft uses 125336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * a different format that excludes the punctuation (this is why I'm not putting this in a 125436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * parent class) 125536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank */ 125636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank public String formatDateTime(Calendar calendar) { 125736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank StringBuilder sb = new StringBuilder(); 125836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank //YYYY-MM-DDTHH:MM:SS.MSSZ 125936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(calendar.get(Calendar.YEAR)); 126036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 126136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MONTH) + 1)); 126236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 126336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.DAY_OF_MONTH))); 126436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('T'); 126536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.HOUR_OF_DAY))); 126636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 126736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MINUTE))); 126836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 126936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.SECOND))); 127036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(".000Z"); 127136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return sb.toString(); 127236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 127336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 1274c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /** 1275c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Note that messages in the deleted database preserve the message's unique id; therefore, we 1276c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * can utilize this id to find references to the message. The only reference situation at this 1277c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * point is in the Body table; it is when sending messages via SmartForward and SmartReply 1278c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 1279c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private boolean messageReferenced(ContentResolver cr, long id) { 1280c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArgument[0] = Long.toString(id); 1281c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // See if this id is referenced in a body 1282c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Body.CONTENT_URI, Body.ID_PROJECTION, WHERE_BODY_SOURCE_MESSAGE_KEY, 1283c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArgument, null); 1284c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank try { 1285c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return c.moveToFirst(); 1286c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } finally { 1287c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank c.close(); 12885acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 1289c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1290c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1291c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /*private*/ /** 1292c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Serialize commands to delete items from the server; as we find items to delete, add their 1293c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * id's to the deletedId's array 1294c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * 1295c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param s the Serializer we're using to create post data 1296c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param deletedIds ids whose deletions are being sent to the server 1297c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param first whether or not this is the first command being sent 1298c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @return true if SYNC_COMMANDS hasn't been sent (false otherwise) 1299c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @throws IOException 1300c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 1301936b0401ac57e2853915bd3535bbd2ab6869c1bbTodd Kennedy @VisibleForTesting 1302c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean sendDeletedItems(Serializer s, ArrayList<Long> deletedIds, boolean first) 1303c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank throws IOException { 1304c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 13055acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank 1306ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find any of our deleted items 1307ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = cr.query(Message.DELETED_CONTENT_URI, Message.LIST_PROJECTION, 1308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 1309ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We keep track of the list of deleted item id's so that we can remove them from the 1310ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // deleted table after the server receives our command 1311c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.clear(); 1312ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1313ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 13145acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = c.getString(Message.LIST_SERVER_ID_COLUMN); 13155acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 13165acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 13175acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 1318c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Also check if this message is referenced elsewhere 1319c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } else if (messageReferenced(cr, c.getLong(Message.CONTENT_ID_COLUMN))) { 1320c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank userLog("Postponing deletion of referenced message: ", serverId); 1321c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank continue; 13225acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } else if (first) { 13237c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank first = false; 1325ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1326ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Send the command to delete this message 13275acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 1328c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.add(c.getLong(Message.LIST_ID_COLUMN)); 1329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1334c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return first; 1335c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1336c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1337c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank @Override 1338c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank public boolean sendLocalChanges(Serializer s) throws IOException { 1339c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 1340c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1341a261805b03b853cce662b679da3e16120d521b7eMarc Blank if (getSyncKey().equals("0")) { 1342a261805b03b853cce662b679da3e16120d521b7eMarc Blank return false; 1343a261805b03b853cce662b679da3e16120d521b7eMarc Blank } 1344a261805b03b853cce662b679da3e16120d521b7eMarc Blank 1345c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Never upsync from these folders 1346c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (mMailbox.mType == Mailbox.TYPE_DRAFTS || mMailbox.mType == Mailbox.TYPE_OUTBOX) { 1347c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return false; 1348c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 1349c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 1350c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // This code is split out for unit testing purposes 1351c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean firstCommand = sendDeletedItems(s, mDeletedIdList, true); 1352c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 13534f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (!mFetchRequestList.isEmpty()) { 13544f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Add FETCH commands for messages that need a body (i.e. we didn't find it during 13554f15001bdfd11c79524b4e44d60041967779e763Marc Blank // our earlier sync; this happens only in EAS 2.5 where the body couldn't be found 13564f15001bdfd11c79524b4e44d60041967779e763Marc Blank // after parsing the message's MIME data) 13574f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (firstCommand) { 13584f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_COMMANDS); 13594f15001bdfd11c79524b4e44d60041967779e763Marc Blank firstCommand = false; 13604f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 13614f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (FetchRequest req: mFetchRequestList) { 13624f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_FETCH).data(Tags.SYNC_SERVER_ID, req.serverId).end(); 13634f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 13644f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 13654f15001bdfd11c79524b4e44d60041967779e763Marc Blank 136691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // Find our trash mailbox, since deletions will have been moved there... 136791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank long trashMailboxId = 13680a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank Mailbox.findMailboxOfType(mContext, mMailbox.mAccountKey, Mailbox.TYPE_TRASH); 136991e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 1370ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Do the same now for updated items 1371c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION, 1372ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 137391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 137491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // We keep track of the list of updated item id's as we did above with deleted items 1375ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.clear(); 1376ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 13779c06bfdc576f9803b45dd009dc4c497366aaaf51Marc Blank ContentValues cv = new ContentValues(); 1378ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 1379ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = c.getLong(Message.LIST_ID_COLUMN); 1380ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Say we've handled this update 1381ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.add(id); 1382ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We have the id of the changed item. But first, we have to find out its current 1383ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // state, since the updated table saves the opriginal state 1384ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor currentCursor = cr.query(ContentUris.withAppendedId(Message.CONTENT_URI, id), 1385ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank UPDATES_PROJECTION, null, null, null); 1386ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 1387ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If this item no longer exists (shouldn't be possible), just move along 1388ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!currentCursor.moveToFirst()) { 1389b2adf8a5aae8647b728441ef4d56f181f26f848eJorge Lugo continue; 1390ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 13915acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 13925acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = currentCursor.getString(UPDATES_SERVER_ID_COLUMN); 13935acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 13945acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 13955acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 139691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 139736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean flagChange = false; 139836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean readChange = false; 139936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 14004471a6960d352242cc65bddf7888cc5335840c74Marc Blank long mailbox = currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN); 14014099db352b1881a403813de8e83ab26f31609df3Marc Blank // If the message is now in the trash folder, it has been deleted by the user 14024099db352b1881a403813de8e83ab26f31609df3Marc Blank if (mailbox == trashMailboxId) { 14034099db352b1881a403813de8e83ab26f31609df3Marc Blank if (firstCommand) { 14044099db352b1881a403813de8e83ab26f31609df3Marc Blank s.start(Tags.SYNC_COMMANDS); 14054099db352b1881a403813de8e83ab26f31609df3Marc Blank firstCommand = false; 14064099db352b1881a403813de8e83ab26f31609df3Marc Blank } 14074099db352b1881a403813de8e83ab26f31609df3Marc Blank // Send the command to delete this message 14084099db352b1881a403813de8e83ab26f31609df3Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 14094099db352b1881a403813de8e83ab26f31609df3Marc Blank // Mark the message as moved (so the copy will be deleted if/when the server 14104099db352b1881a403813de8e83ab26f31609df3Marc Blank // version is synced) 14114099db352b1881a403813de8e83ab26f31609df3Marc Blank int flags = c.getInt(Message.LIST_FLAGS_COLUMN); 14124099db352b1881a403813de8e83ab26f31609df3Marc Blank cv.put(MessageColumns.FLAGS, 14134099db352b1881a403813de8e83ab26f31609df3Marc Blank flags | EasSyncService.MESSAGE_FLAG_MOVED_MESSAGE); 14144099db352b1881a403813de8e83ab26f31609df3Marc Blank cr.update(ContentUris.withAppendedId(Message.CONTENT_URI, id), cv, 14154099db352b1881a403813de8e83ab26f31609df3Marc Blank null, null); 14164099db352b1881a403813de8e83ab26f31609df3Marc Blank continue; 14174099db352b1881a403813de8e83ab26f31609df3Marc Blank } else if (mailbox != c.getLong(Message.LIST_MAILBOX_KEY_COLUMN)) { 14184471a6960d352242cc65bddf7888cc5335840c74Marc Blank // The message has moved to another mailbox; add a request for this 14194471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Note: The Sync command doesn't handle moving messages, so we need 14204471a6960d352242cc65bddf7888cc5335840c74Marc Blank // to handle this as a "request" (similar to meeting response and 14214471a6960d352242cc65bddf7888cc5335840c74Marc Blank // attachment load) 14224471a6960d352242cc65bddf7888cc5335840c74Marc Blank mService.addRequest(new MessageMoveRequest(id, mailbox)); 14234471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Regardless of other changes that might be made, we don't want to indicate 14244471a6960d352242cc65bddf7888cc5335840c74Marc Blank // that this message has been updated until the move request has been 14254471a6960d352242cc65bddf7888cc5335840c74Marc Blank // handled (without this, a crash between the flag upsync and the move 14264471a6960d352242cc65bddf7888cc5335840c74Marc Blank // would cause the move to be lost) 14274471a6960d352242cc65bddf7888cc5335840c74Marc Blank mUpdatedIdList.remove(id); 14284471a6960d352242cc65bddf7888cc5335840c74Marc Blank } 142936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 143036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // We can only send flag changes to the server in 12.0 or later 14314471a6960d352242cc65bddf7888cc5335840c74Marc Blank int flag = 0; 1432d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 143336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flag = currentCursor.getInt(UPDATES_FLAG_COLUMN); 143436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != c.getInt(Message.LIST_FAVORITE_COLUMN)) { 143536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flagChange = true; 143636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 143736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 143836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 143991e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank int read = currentCursor.getInt(UPDATES_READ_COLUMN); 144076eb7b252fc410f5f5d4e90ad54d4bde837de0aaMarc Blank if (read != c.getInt(Message.LIST_READ_COLUMN)) { 144136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank readChange = true; 144236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 144336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 144436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (!flagChange && !readChange) { 144536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // In this case, we've got nothing to send to the server 1446ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 1447ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 144836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 1449c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (firstCommand) { 14507c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1451c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank firstCommand = false; 1452ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 145336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Send the change to "read" and "favorite" (flagged) 14547c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_CHANGE) 14557c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.SYNC_SERVER_ID, c.getString(Message.LIST_SERVER_ID_COLUMN)) 145636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank .start(Tags.SYNC_APPLICATION_DATA); 145736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (readChange) { 145836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_READ, Integer.toString(read)); 145936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 146036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "Flag" is a relatively complex concept in EAS 12.0 and above. It is not only 146136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // the boolean "favorite" that we think of in Gmail, but it also represents a 146236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // follow up action, which can include a subject, start and due dates, and even 146336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // recurrences. We don't support any of this as yet, but EAS 12.0 and higher 146436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // require that a flag contain a status, a type, and four date fields, two each 146536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // for start date and end (due) date. 146636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flagChange) { 146736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != 0) { 146836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Status 2 = set flag 146936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.start(Tags.EMAIL_FLAG).data(Tags.EMAIL_FLAG_STATUS, "2"); 147036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "FollowUp" is the standard type 147136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_FLAG_TYPE, "FollowUp"); 147236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank long now = System.currentTimeMillis(); 147336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank Calendar calendar = 147436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT")); 147536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now); 147636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Flags are required to have a start date and end date (duplicated) 147736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // First, we'll set the current date/time in GMT as the start time 147836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank String utc = formatDateTime(calendar); 147936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_START_DATE, utc).data(Tags.TASK_UTC_START_DATE, utc); 148036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // And then we'll use one week from today for completion date 148136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now + 1*WEEKS); 148236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank utc = formatDateTime(calendar); 148336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_DUE_DATE, utc).data(Tags.TASK_UTC_DUE_DATE, utc); 148436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end(); 148536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else { 148636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.tag(Tags.EMAIL_FLAG); 148736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 148836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 148936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE 1490ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1491ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank currentCursor.close(); 1492ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1493ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1494ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1495ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1496ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1497ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1498c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (!firstCommand) { 14997c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.end(); // SYNC_COMMANDS 1500ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1501ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return false; 1502ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1503ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank} 1504