EmailSyncAdapter.java revision 068e073ffcb68e06785929208d6a6761b29030f3
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 20068e073ffcb68e06785929208d6a6761b29030f3Marc Blankimport com.android.email.LegacyConversions; 218e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Predaimport com.android.email.Utility; 2267698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.mail.Address; 235c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.email.mail.MeetingInfo; 244f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport com.android.email.mail.MessagingException; 255c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blankimport com.android.email.mail.PackedString; 264f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport com.android.email.mail.Part; 274f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport com.android.email.mail.internet.MimeMessage; 28068e073ffcb68e06785929208d6a6761b29030f3Marc Blankimport com.android.email.mail.internet.MimeUtility; 291f19b92899b3589e5d6303a7dd35ebbf569e00b9Marc Blankimport com.android.email.provider.AttachmentProvider; 30c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blankimport com.android.email.provider.EmailContent; 3100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.email.provider.EmailProvider; 32894ec76e35053ab73ee9905b6737910e7fab7cd9Andrew Stadlerimport com.android.email.provider.EmailContent.Account; 33c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blankimport com.android.email.provider.EmailContent.AccountColumns; 3467698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Attachment; 35c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blankimport com.android.email.provider.EmailContent.Body; 3667698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Mailbox; 3767698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Message; 3867698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.MessageColumns; 3967698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.SyncColumns; 40a1e128b4676c1a4fb583b61ea94e561c6045e6f8Marc Blankimport com.android.email.service.MailService; 4100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.Eas; 4200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.EasSyncService; 434471a6960d352242cc65bddf7888cc5335840c74Marc Blankimport com.android.exchange.MessageMoveRequest; 44c10a3beef4f048292e6a4ceb31527c5123801517Marc Blankimport com.android.exchange.utility.CalendarUtilities; 45ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 46ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentProviderOperation; 47ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentResolver; 48ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentUris; 49ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentValues; 50ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.OperationApplicationException; 51ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.database.Cursor; 5277424af660458104b732bdcb718874b17d0cab3aMarc Blankimport android.net.Uri; 53ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.os.RemoteException; 5400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport android.webkit.MimeTypeMap; 55ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 564f15001bdfd11c79524b4e44d60041967779e763Marc Blankimport java.io.ByteArrayInputStream; 5700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException; 5800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream; 5900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList; 6036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blankimport java.util.Calendar; 6100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.GregorianCalendar; 6200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.TimeZone; 63ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 64ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/** 65ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Sync adapter for EAS email 66ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * 67ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */ 687c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankpublic class EmailSyncAdapter extends AbstractSyncAdapter { 6900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 7091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_READ_COLUMN = 0; 7191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_MAILBOX_KEY_COLUMN = 1; 7291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final int UPDATES_SERVER_ID_COLUMN = 2; 7336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private static final int UPDATES_FLAG_COLUMN = 3; 7491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank private static final String[] UPDATES_PROJECTION = 7536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank {MessageColumns.FLAG_READ, MessageColumns.MAILBOX_KEY, SyncColumns.SERVER_ID, 7636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank MessageColumns.FLAG_FAVORITE}; 7749c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank 7849c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_ID_COLUMN = 0; 7949c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank private static final int MESSAGE_ID_SUBJECT_SUBJECT_COLUMN = 1; 8018e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank private static final String[] MESSAGE_ID_SUBJECT_PROJECTION = 8118e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank new String[] { Message.RECORD_ID, MessageColumns.SUBJECT }; 8218e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank 83c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private static final String WHERE_BODY_SOURCE_MESSAGE_KEY = Body.SOURCE_MESSAGE_KEY + "=?"; 844f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String[] FETCH_REQUEST_PROJECTION = 854f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {EmailContent.RECORD_ID, SyncColumns.SERVER_ID}; 864f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_RECORD_ID = 0; 874f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final int FETCH_REQUEST_SERVER_ID = 1; 884f15001bdfd11c79524b4e44d60041967779e763Marc Blank 894f15001bdfd11c79524b4e44d60041967779e763Marc Blank private static final String EMAIL_WINDOW_SIZE = "5"; 9091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 91c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank String[] mBindArguments = new String[2]; 92c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank String[] mBindArgument = new String[1]; 93ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 944f15001bdfd11c79524b4e44d60041967779e763Marc Blank /*package*/ ArrayList<Long> mDeletedIdList = new ArrayList<Long>(); 954f15001bdfd11c79524b4e44d60041967779e763Marc Blank /*package*/ ArrayList<Long> mUpdatedIdList = new ArrayList<Long>(); 964f15001bdfd11c79524b4e44d60041967779e763Marc Blank /*package*/ ArrayList<FetchRequest> mFetchRequestList = new ArrayList<FetchRequest>(); 974f15001bdfd11c79524b4e44d60041967779e763Marc Blank private boolean mFetchNeeded = false; 98ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 998efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Holds the parser's value for isLooping() 1008efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank boolean mIsLooping = false; 1018efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 1027c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank public EmailSyncAdapter(Mailbox mailbox, EasSyncService service) { 103147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank super(mailbox, service); 104ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 105ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1064f15001bdfd11c79524b4e44d60041967779e763Marc Blank private String getEmailFilter() { 1074f15001bdfd11c79524b4e44d60041967779e763Marc Blank switch (mAccount.mSyncLookback) { 1084f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_1_DAY: 1094f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_DAY; 1104f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_3_DAYS: 1114f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_3_DAYS; 1124f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_1_WEEK: 1134f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 1144f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_2_WEEKS: 1154f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_2_WEEKS; 1164f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_1_MONTH: 1174f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_MONTH; 1184f15001bdfd11c79524b4e44d60041967779e763Marc Blank case com.android.email.Account.SYNC_WINDOW_ALL: 1194f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_ALL; 1204f15001bdfd11c79524b4e44d60041967779e763Marc Blank default: 1214f15001bdfd11c79524b4e44d60041967779e763Marc Blank return Eas.FILTER_1_WEEK; 1224f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1234f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1244f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1254f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 1264f15001bdfd11c79524b4e44d60041967779e763Marc Blank * Holder for fetch request information (record id and server id) 1274f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 1284f15001bdfd11c79524b4e44d60041967779e763Marc Blank static class FetchRequest { 1294f15001bdfd11c79524b4e44d60041967779e763Marc Blank final long messageId; 1304f15001bdfd11c79524b4e44d60041967779e763Marc Blank final String serverId; 1314f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1324f15001bdfd11c79524b4e44d60041967779e763Marc Blank FetchRequest(long _messageId, String _serverId) { 1334f15001bdfd11c79524b4e44d60041967779e763Marc Blank messageId = _messageId; 1344f15001bdfd11c79524b4e44d60041967779e763Marc Blank serverId = _serverId; 1354f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1364f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1374f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1384f15001bdfd11c79524b4e44d60041967779e763Marc Blank @Override 1394f15001bdfd11c79524b4e44d60041967779e763Marc Blank public void sendSyncOptions(Double protocolVersion, Serializer s) 1404f15001bdfd11c79524b4e44d60041967779e763Marc Blank throws IOException { 1414f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.clear(); 1424f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find partially loaded messages; this should typically be a rare occurrence 1434f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = mContext.getContentResolver().query(Message.CONTENT_URI, 1444f15001bdfd11c79524b4e44d60041967779e763Marc Blank FETCH_REQUEST_PROJECTION, 1454f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.FLAG_LOADED + "=" + Message.FLAG_LOADED_PARTIAL + " AND " + 1464f15001bdfd11c79524b4e44d60041967779e763Marc Blank MessageColumns.MAILBOX_KEY + "=?", 1474f15001bdfd11c79524b4e44d60041967779e763Marc Blank new String[] {Long.toString(mMailbox.mId)}, null); 1484f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 1494f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Put all of these messages into a list; we'll need both id and server id 1504f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (c.moveToNext()) { 1514f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchRequestList.add(new FetchRequest(c.getLong(FETCH_REQUEST_RECORD_ID), 1524f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.getString(FETCH_REQUEST_SERVER_ID))); 1534f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1544f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 1554f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 1564f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1574f15001bdfd11c79524b4e44d60041967779e763Marc Blank 1584f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The "empty" case is typical; we send a request for changes, and also specify a sync 1594f15001bdfd11c79524b4e44d60041967779e763Marc Blank // window, body preference type (HTML for EAS 12.0 and later; MIME for EAS 2.5), and 1604f15001bdfd11c79524b4e44d60041967779e763Marc Blank // truncation 1614f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If there are fetch requests, we only want the fetches (i.e. no changes from the server) 1624f15001bdfd11c79524b4e44d60041967779e763Marc Blank // so we turn MIME support off. Note that we are always using EAS 2.5 if there are fetch 1634f15001bdfd11c79524b4e44d60041967779e763Marc Blank // requests 1644f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchRequestList.isEmpty()) { 1654f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.tag(Tags.SYNC_DELETES_AS_MOVES); 1664f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.tag(Tags.SYNC_GET_CHANGES); 1674f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_WINDOW_SIZE, EMAIL_WINDOW_SIZE); 1684f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 1694f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the lookback appropriately (EAS calls this a "filter") 1704f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_FILTER_TYPE, getEmailFilter()); 1714f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Set the truncation amount for all classes 1724f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (protocolVersion >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 1734f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.BASE_BODY_PREFERENCE); 1744f15001bdfd11c79524b4e44d60041967779e763Marc Blank // HTML for email 1754f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML); 1764f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.BASE_TRUNCATION_SIZE, Eas.EAS12_TRUNCATION_SIZE); 1774f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 1784f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 1794f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Use MIME data for EAS 2.5 1804f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_MIME); 1814f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 1824f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1834f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 1844f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else { 1854f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_OPTIONS); 1864f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Ask for plain text, rather than MIME data. This guarantees that we'll get a usable 1874f15001bdfd11c79524b4e44d60041967779e763Marc Blank // text body 1884f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_MIME_SUPPORT, Eas.MIME_BODY_PREFERENCE_TEXT); 1894f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.data(Tags.SYNC_TRUNCATION, Eas.EAS2_5_TRUNCATION_SIZE); 1904f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.end(); 1914f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1924f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 1934f15001bdfd11c79524b4e44d60041967779e763Marc Blank 194ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 19548af7392c82262d17700e3fbdccf3a582809d449Marc Blank public boolean parse(InputStream is) throws IOException { 19648af7392c82262d17700e3fbdccf3a582809d449Marc Blank EasEmailSyncParser p = new EasEmailSyncParser(is, this); 1974f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = false; 1988efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank boolean res = p.parse(); 1998efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank // Hold on to the parser's value for isLooping() to pass back to the service 2008efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank mIsLooping = p.isLooping(); 2014f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we've need a body fetch, or we've just finished one, return true in order to continue 2024f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (mFetchNeeded || !mFetchRequestList.isEmpty()) { 2034f15001bdfd11c79524b4e44d60041967779e763Marc Blank return true; 2044f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 2058efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return res; 2068efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank } 2078efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank 2088efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank /** 2098efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank * Return the value of isLooping() as returned from the parser 2108efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank */ 2118efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank @Override 2128efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank public boolean isLooping() { 2138efd25be4e1db3c0c79aae2ca1b4664b21bb410bMarc Blank return mIsLooping; 214ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 215147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 216aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank @Override 217aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank public boolean isSyncable() { 218aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank return true; 219aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank } 220aa288fe7ccbd28abcf990ce8337f2da677a1d370Marc Blank 221368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank public class EasEmailSyncParser extends AbstractSyncParser { 222ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 223147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY = 224ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?"; 225ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 226ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private String mMailboxIdAsString; 227ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 22848af7392c82262d17700e3fbdccf3a582809d449Marc Blank ArrayList<Message> newEmails = new ArrayList<Message>(); 2294f15001bdfd11c79524b4e44d60041967779e763Marc Blank ArrayList<Message> fetchedEmails = new ArrayList<Message>(); 23048af7392c82262d17700e3fbdccf3a582809d449Marc Blank ArrayList<Long> deletedEmails = new ArrayList<Long>(); 23148af7392c82262d17700e3fbdccf3a582809d449Marc Blank ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>(); 23248af7392c82262d17700e3fbdccf3a582809d449Marc Blank 23348af7392c82262d17700e3fbdccf3a582809d449Marc Blank public EasEmailSyncParser(InputStream in, EmailSyncAdapter adapter) throws IOException { 23448af7392c82262d17700e3fbdccf3a582809d449Marc Blank super(in, adapter); 235ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mMailboxIdAsString = Long.toString(mMailbox.mId); 236ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 237ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 238147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank @Override 239ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void wipe() { 240ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mContentResolver.delete(Message.CONTENT_URI, 241ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 242ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mContentResolver.delete(Message.DELETED_CONTENT_URI, 243ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 244ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mContentResolver.delete(Message.UPDATED_CONTENT_URI, 245ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Message.MAILBOX_KEY + "=" + mMailbox.mId, null); 246ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 247ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 248ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void addData (Message msg) throws IOException { 249ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<Attachment> atts = new ArrayList<Attachment>(); 2504f15001bdfd11c79524b4e44d60041967779e763Marc Blank boolean truncated = false; 251ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 2527c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 253ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 2547c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATTACHMENTS: 2555ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up 256d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank attachmentsParser(atts, msg); 257ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2587c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_TO: 25967698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mTo = Address.pack(Address.parse(getValue())); 260ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2617c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FROM: 262705a309bd78efd77469ac90a57849619de3317e3Mihai Preda Address[] froms = Address.parse(getValue()); 263705a309bd78efd77469ac90a57849619de3317e3Mihai Preda if (froms != null && froms.length > 0) { 2644f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mDisplayName = froms[0].toFriendly(); 265ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 266705a309bd78efd77469ac90a57849619de3317e3Mihai Preda msg.mFrom = Address.pack(froms); 267ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2687c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_CC: 26967698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mCc = Address.pack(Address.parse(getValue())); 270ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2717c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_REPLY_TO: 27267698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mReplyTo = Address.pack(Address.parse(getValue())); 273ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DATE_RECEIVED: 2758e26c42accbaf72eff6694173496aba0e6aa37f6Mihai Preda msg.mTimeStamp = Utility.parseEmailDateTimeToMillis(getValue()); 276ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2777c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_SUBJECT: 278ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mSubject = getValue(); 279ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2807c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 281ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagRead = getValueInt() == 1; 282ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 2837c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_BODY: 28400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyParser(msg); 28500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 2867c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 287ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank msg.mFlagFavorite = flagParser(); 288ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 2894f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_TRUNCATED: 2904f15001bdfd11c79524b4e44d60041967779e763Marc Blank truncated = getValueInt() == 1; 2914f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 2924f15001bdfd11c79524b4e44d60041967779e763Marc Blank case Tags.EMAIL_MIME_DATA: 2934f15001bdfd11c79524b4e44d60041967779e763Marc Blank // We get MIME data for EAS 2.5. First we parse it, then we take the 2944f15001bdfd11c79524b4e44d60041967779e763Marc Blank // html and/or plain text data and store it in the message 295068e073ffcb68e06785929208d6a6761b29030f3Marc Blank if (truncated) { 296068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // If the MIME data is truncated, don't bother parsing it, because 297068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // it will take time and throw an exception anyway when EOF is reached 298068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // In this case, we will load the body separately by tagging the message 299068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // "partially loaded". 3004f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Partially loaded: ", msg.mServerId); 3014f15001bdfd11c79524b4e44d60041967779e763Marc Blank msg.mFlagLoaded = Message.FLAG_LOADED_PARTIAL; 3024f15001bdfd11c79524b4e44d60041967779e763Marc Blank mFetchNeeded = true; 303068e073ffcb68e06785929208d6a6761b29030f3Marc Blank } else { 304068e073ffcb68e06785929208d6a6761b29030f3Marc Blank mimeBodyParser(msg, getValue()); 3054f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 3064f15001bdfd11c79524b4e44d60041967779e763Marc Blank break; 3077c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_BODY: 30867698e240187c902bed123bf18d342ff25ec75c7Marc Blank String text = getValue(); 30967698e240187c902bed123bf18d342ff25ec75c7Marc Blank msg.mText = text; 310ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 3117531be7774769c84b499b1de5dc46da3a9468316Marc Blank case Tags.EMAIL_MESSAGE_CLASS: 3127531be7774769c84b499b1de5dc46da3a9468316Marc Blank String messageClass = getValue(); 3137531be7774769c84b499b1de5dc46da3a9468316Marc Blank if (messageClass.equals("IPM.Schedule.Meeting.Request")) { 314888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_INVITE; 3157531be7774769c84b499b1de5dc46da3a9468316Marc Blank } else if (messageClass.equals("IPM.Schedule.Meeting.Canceled")) { 316888ddc50a9f6a05d2f4076bada11085ddcfb8aefAndrew Stadler msg.mFlags |= Message.FLAG_INCOMING_MEETING_CANCEL; 3177531be7774769c84b499b1de5dc46da3a9468316Marc Blank } 3187531be7774769c84b499b1de5dc46da3a9468316Marc Blank break; 319c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_MEETING_REQUEST: 320c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank meetingRequestParser(msg); 321c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 322ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 323ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 325ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 326ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 327ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (atts.size() > 0) { 328ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAttachments = atts; 329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 33167698e240187c902bed123bf18d342ff25ec75c7Marc Blank 3325c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank /** 3335c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * Set up the meetingInfo field in the message with various pieces of information gleaned 3345c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * from MeetingRequest tags. This information will be used later to generate an appropriate 3355c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * reply email if the user chooses to respond 3365c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @param msg the Message being built 3375c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank * @throws IOException 3385c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank */ 339c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void meetingRequestParser(Message msg) throws IOException { 3405c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank PackedString.Builder packedString = new PackedString.Builder(); 341c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_MEETING_REQUEST) != END) { 342c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 3435c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_DTSTAMP: 3445c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTAMP, getValue()); 3455c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 346c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_START_TIME: 3475c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTSTART, getValue()); 3485c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 3495c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_END_TIME: 3505c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_DTEND, getValue()); 3515c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 3525c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_ORGANIZER: 3535c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_ORGANIZER_EMAIL, getValue()); 3545c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 3555c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_LOCATION: 3565c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_LOCATION, getValue()); 3575c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank break; 3585c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank case Tags.EMAIL_GLOBAL_OBJID: 359b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank packedString.put(MeetingInfo.MEETING_UID, 360b94d16528fc9c7f5dbb5c75c059f76cee2070c09Marc Blank CalendarUtilities.getUidFromGlobalObjId(getValue())); 361c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 362c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_CATEGORIES: 363c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank nullParser(); 364c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 365c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCES: 366c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank recurrencesParser(); 367c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank break; 368c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 369c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 370c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 371c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 3725c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank if (msg.mSubject != null) { 3735c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank packedString.put(MeetingInfo.MEETING_TITLE, msg.mSubject); 3745c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank } 3755c6e14ab2f2e4c5dfc97cdeaedcc105159a9f29cMarc Blank msg.mMeetingInfo = packedString.toString(); 376c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 377c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 378c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void nullParser() throws IOException { 379c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_CATEGORIES) != END) { 380c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 381c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 382c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 383c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 384c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank private void recurrencesParser() throws IOException { 385c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank while (nextTag(Tags.EMAIL_RECURRENCES) != END) { 386c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank switch (tag) { 387c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank case Tags.EMAIL_RECURRENCE: 388c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank nullParser(); 389f69266d6671aa2c55fd04117a36f74dd17f73067Marc Blank break; 390c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank default: 391c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank skipTag(); 392c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 393c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 394c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank } 395c10a3beef4f048292e6a4ceb31527c5123801517Marc Blank 39600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void addParser(ArrayList<Message> emails) throws IOException { 397ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Message msg = new Message(); 398ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mAccountKey = mAccount.mId; 399ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mMailboxKey = mMailbox.mId; 400f02459a766ddb1727d191daa0aeb559c8f848668Andrew Stadler msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE; 401ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 4027c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_ADD) != END) { 403ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 4047c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 405ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mServerId = getValue(); 406ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 4077c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_APPLICATION_DATA: 408ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank addData(msg); 409ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 410ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 411ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 412ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 413ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 414ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank emails.add(msg); 415ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 416ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 417ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank // For now, we only care about the "active" state 418ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank private Boolean flagParser() throws IOException { 419ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean state = false; 4207c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_FLAG) != END) { 421ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank switch (tag) { 4227c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG_STATUS: 423ce17455fc5abf061e252d495288d0d56404b0b62Marc Blank state = getValueInt() == 2; 424ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 425ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank default: 426ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank skipTag(); 427ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 428ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 429ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank return state; 430ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 431ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank 43200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void bodyParser(Message msg) throws IOException { 43300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String bodyType = Eas.BODY_PREFERENCE_TEXT; 43400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String body = ""; 4357c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_BODY) != END) { 43600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank switch (tag) { 4377c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_TYPE: 43800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank bodyType = getValue(); 43900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 4407c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.BASE_DATA: 44100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank body = getValue(); 44200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank break; 44300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank default: 44400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank skipTag(); 44500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 44600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 44700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // We always ask for TEXT or HTML; there's no third option 44800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) { 44900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mHtml = body; 45000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 45100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank msg.mText = body; 45200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 45300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 45400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 4554f15001bdfd11c79524b4e44d60041967779e763Marc Blank /** 456068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * Parses untruncated MIME data, saving away the text parts 4574f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param msg the message we're building 4584f15001bdfd11c79524b4e44d60041967779e763Marc Blank * @param mimeData the MIME data we've received from the server 459068e073ffcb68e06785929208d6a6761b29030f3Marc Blank * @throws IOException 4604f15001bdfd11c79524b4e44d60041967779e763Marc Blank */ 461068e073ffcb68e06785929208d6a6761b29030f3Marc Blank private void mimeBodyParser(Message msg, String mimeData) throws IOException { 4624f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 4634f15001bdfd11c79524b4e44d60041967779e763Marc Blank ByteArrayInputStream in = new ByteArrayInputStream(mimeData.getBytes()); 4644f15001bdfd11c79524b4e44d60041967779e763Marc Blank // The constructor parses the message 465068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeMessage mimeMessage = new MimeMessage(in); 466068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // Now process body parts & attachments 467068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> viewables = new ArrayList<Part>(); 468068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // We'll ignore the attachments, as we'll get them directly from EAS 469068e073ffcb68e06785929208d6a6761b29030f3Marc Blank ArrayList<Part> attachments = new ArrayList<Part>(); 470068e073ffcb68e06785929208d6a6761b29030f3Marc Blank MimeUtility.collectParts(mimeMessage, viewables, attachments); 471068e073ffcb68e06785929208d6a6761b29030f3Marc Blank Body tempBody = new Body(); 472068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // updateBodyFields fills in the content fields of the Body 473068e073ffcb68e06785929208d6a6761b29030f3Marc Blank LegacyConversions.updateBodyFields(tempBody, msg, viewables); 474068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // But we need them in the message itself for handling during commit() 475068e073ffcb68e06785929208d6a6761b29030f3Marc Blank msg.mHtml = tempBody.mHtmlContent; 476068e073ffcb68e06785929208d6a6761b29030f3Marc Blank msg.mText = tempBody.mTextContent; 4774f15001bdfd11c79524b4e44d60041967779e763Marc Blank } catch (MessagingException e) { 478068e073ffcb68e06785929208d6a6761b29030f3Marc Blank // This would most likely indicate a broken stream 479068e073ffcb68e06785929208d6a6761b29030f3Marc Blank throw new IOException(e); 4804f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 4814f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 4824f15001bdfd11c79524b4e44d60041967779e763Marc Blank 4835ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank private void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException { 4845ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENTS) != END) { 4855ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank switch (tag) { 4865ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.EMAIL_ATTACHMENT: 4875ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ATTACHMENT: // BASE_ATTACHMENT is used in EAS 12.0 and up 4885ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank attachmentParser(atts, msg); 4895ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank break; 4905ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank default: 4915ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank skipTag(); 4925ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 4935ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 4945ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank } 4955ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank 49600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank private void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException { 497ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String fileName = null; 498ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String length = null; 499d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank String location = null; 500ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 5017c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.EMAIL_ATTACHMENT) != END) { 502ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 5035ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank // We handle both EAS 2.5 and 12.0+ attachments here 5047c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_DISPLAY_NAME: 5055ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_DISPLAY_NAME: 506ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank fileName = getValue(); 507ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5087c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_NAME: 5095ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_FILE_REFERENCE: 510d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank location = getValue(); 511ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 5127c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_ATT_SIZE: 5135ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank case Tags.BASE_ESTIMATED_DATA_SIZE: 514ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank length = getValue(); 515ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 516ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 517ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 518ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 519ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 520ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 5215ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((fileName != null) && (length != null) && (location != null)) { 522ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Attachment att = new Attachment(); 523ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mEncoding = "base64"; 524ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mSize = Long.parseLong(length); 525ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank att.mFileName = fileName; 526d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank att.mLocation = location; 52700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank att.mMimeType = getMimeTypeFromFileName(fileName); 528ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank atts.add(att); 529ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank msg.mFlagAttachment = true; 530ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 531ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 532ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 53300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /** 53400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * Try to determine a mime type from a file name, defaulting to application/x, where x 53500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * is either the extension or (if none) octet-stream 53600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * At the moment, this is somewhat lame, since many file types aren't recognized 53700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @param fileName the file name to ponder 53800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @return 53900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 54000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // Note: The MimeTypeMap method currently uses a very limited set of mime types 54100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A bug has been filed against this issue. 54200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank public String getMimeTypeFromFileName(String fileName) { 54300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String mimeType; 54400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank int lastDot = fileName.lastIndexOf('.'); 54500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank String extension = null; 5465ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if ((lastDot > 0) && (lastDot < fileName.length() - 1)) { 54719fd685351de80c62c9bc7f0f05fe96983a8078dMarc Blank extension = fileName.substring(lastDot + 1).toLowerCase(); 54800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 54900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (extension == null) { 55000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank // A reasonable default for now. 55100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/octet-stream"; 55200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } else { 55300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension); 55400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank if (mimeType == null) { 55500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank mimeType = "application/" + extension; 55600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 55700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 55800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank return mimeType; 55900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank } 56000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank 561ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank private Cursor getServerIdCursor(String serverId, String[] projection) { 562c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArguments[0] = serverId; 563c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArguments[1] = mMailboxIdAsString; 564ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return mContentResolver.query(Message.CONTENT_URI, projection, 565c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank WHERE_SERVER_ID_AND_MAILBOX_KEY, mBindArguments, null); 566ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 567ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 56802e3436296ad5a638a1b6f349555e39964c6d13dMarc Blank /*package*/ void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException { 569048d45641b88c87172074aa5f29b3de307bc3712Marc Blank while (nextTag(entryTag) != END) { 570ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 5717c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 572ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = getValue(); 573ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find the message in this mailbox with the given serverId 57418e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank Cursor c = getServerIdCursor(serverId, MESSAGE_ID_SUBJECT_PROJECTION); 575ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 576ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 57749c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank deletes.add(c.getLong(MESSAGE_ID_SUBJECT_ID_COLUMN)); 57818e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank if (Eas.USER_LOG) { 57949c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank userLog("Deleting ", serverId + ", " 58049c739b7b7e0663b9a292b0d0ec966814aebe42aMarc Blank + c.getString(MESSAGE_ID_SUBJECT_SUBJECT_COLUMN)); 58118e1e20e3c5e098fd4c038349dddb6112aa130edMarc Blank } 582ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 583ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 584ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 585ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 586ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 587ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 588ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 589ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 590ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 591ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 592ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 593ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank class ServerChange { 594ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id; 595ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean read; 596ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean flag; 597ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 598ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank ServerChange(long _id, Boolean _read, Boolean _flag) { 599ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = _id; 600ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = _read; 601ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = _flag; 602ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 603ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 604ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 60502e3436296ad5a638a1b6f349555e39964c6d13dMarc Blank /*package*/ void changeParser(ArrayList<ServerChange> changes) throws IOException { 606ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank String serverId = null; 607ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldRead = false; 608ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank Boolean oldFlag = false; 609ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = 0; 6107c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_CHANGE) != END) { 611ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank switch (tag) { 6127c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.SYNC_SERVER_ID: 613ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank serverId = getValue(); 614ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = getServerIdCursor(serverId, Message.LIST_PROJECTION); 615ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 616ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (c.moveToFirst()) { 6170a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog("Changing ", serverId); 618ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank oldRead = c.getInt(Message.LIST_READ_COLUMN) == Message.READ; 619ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank oldFlag = c.getInt(Message.LIST_FAVORITE_COLUMN) == 1; 620ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank id = c.getLong(Message.LIST_ID_COLUMN); 621ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 622ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 623ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 624ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 625ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 626d82abe7213806e478338af4202b3622f34b5d6feMarc Blank case Tags.SYNC_APPLICATION_DATA: 627d82abe7213806e478338af4202b3622f34b5d6feMarc Blank changeApplicationDataParser(changes, oldRead, oldFlag, id); 628d82abe7213806e478338af4202b3622f34b5d6feMarc Blank break; 629d82abe7213806e478338af4202b3622f34b5d6feMarc Blank default: 630d82abe7213806e478338af4202b3622f34b5d6feMarc Blank skipTag(); 631d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 632d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 633d82abe7213806e478338af4202b3622f34b5d6feMarc Blank } 634d82abe7213806e478338af4202b3622f34b5d6feMarc Blank 635d82abe7213806e478338af4202b3622f34b5d6feMarc Blank private void changeApplicationDataParser(ArrayList<ServerChange> changes, Boolean oldRead, 636d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean oldFlag, long id) throws IOException { 637d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean read = null; 638d82abe7213806e478338af4202b3622f34b5d6feMarc Blank Boolean flag = null; 639d82abe7213806e478338af4202b3622f34b5d6feMarc Blank while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) { 640d82abe7213806e478338af4202b3622f34b5d6feMarc Blank switch (tag) { 6417c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_READ: 642ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank read = getValueInt() == 1; 643ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank break; 6447c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank case Tags.EMAIL_FLAG: 645ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank flag = flagParser(); 646ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank break; 647ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank default: 648ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 649ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 650ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 6515ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank if (((read != null) && !oldRead.equals(read)) || 6525ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank ((flag != null) && !oldFlag.equals(flag))) { 653ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank changes.add(new ServerChange(id, read, flag)); 654ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 655ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 656ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 65700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank /* (non-Javadoc) 65800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank * @see com.android.exchange.adapter.EasContentParser#commandsParser() 65900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank */ 660147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank @Override 661ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public void commandsParser() throws IOException { 6627c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank while (nextTag(Tags.SYNC_COMMANDS) != END) { 6637c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank if (tag == Tags.SYNC_ADD) { 664ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank addParser(newEmails); 6650a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 666048d45641b88c87172074aa5f29b3de307bc3712Marc Blank } else if (tag == Tags.SYNC_DELETE || tag == Tags.SYNC_SOFT_DELETE) { 667048d45641b88c87172074aa5f29b3de307bc3712Marc Blank deleteParser(deletedEmails, tag); 6680a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 6697c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank } else if (tag == Tags.SYNC_CHANGE) { 670ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank changeParser(changedEmails); 6710a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank incrementChangeCount(); 672ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } else 673ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank skipTag(); 674ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 67548af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 67648af7392c82262d17700e3fbdccf3a582809d449Marc Blank 67748af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 6784f15001bdfd11c79524b4e44d60041967779e763Marc Blank public void responsesParser() throws IOException { 6794f15001bdfd11c79524b4e44d60041967779e763Marc Blank while (nextTag(Tags.SYNC_RESPONSES) != END) { 6804f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (tag == Tags.SYNC_ADD || tag == Tags.SYNC_CHANGE || tag == Tags.SYNC_DELETE) { 6814f15001bdfd11c79524b4e44d60041967779e763Marc Blank // We can ignore all of these 6824f15001bdfd11c79524b4e44d60041967779e763Marc Blank } else if (tag == Tags.SYNC_FETCH) { 6834f15001bdfd11c79524b4e44d60041967779e763Marc Blank addParser(fetchedEmails); 6844f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 6854f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 68648af7392c82262d17700e3fbdccf3a582809d449Marc Blank } 68748af7392c82262d17700e3fbdccf3a582809d449Marc Blank 68848af7392c82262d17700e3fbdccf3a582809d449Marc Blank @Override 689a1e1f139046dfb28ea69c46d392f70764ad6f822Andrew Stadler public void commit() { 69048af7392c82262d17700e3fbdccf3a582809d449Marc Blank int notifyCount = 0; 691ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 692ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Use a batch operation to handle the changes 693ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // TODO New mail notifications? Who looks for these? 694ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 6954f15001bdfd11c79524b4e44d60041967779e763Marc Blank 6964f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (Message msg: fetchedEmails) { 6974f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If there's really no text, we're done here 6984f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (msg.mText == null) continue; 6994f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Find the original message's id (by serverId and mailbox) 7004f15001bdfd11c79524b4e44d60041967779e763Marc Blank Cursor c = getServerIdCursor(msg.mServerId, EmailContent.ID_PROJECTION); 7014f15001bdfd11c79524b4e44d60041967779e763Marc Blank String id = null; 7024f15001bdfd11c79524b4e44d60041967779e763Marc Blank try { 7034f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (c.moveToFirst()) { 7044f15001bdfd11c79524b4e44d60041967779e763Marc Blank id = c.getString(EmailContent.ID_PROJECTION_COLUMN); 7054f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7064f15001bdfd11c79524b4e44d60041967779e763Marc Blank } finally { 7074f15001bdfd11c79524b4e44d60041967779e763Marc Blank c.close(); 7084f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7094f15001bdfd11c79524b4e44d60041967779e763Marc Blank 7104f15001bdfd11c79524b4e44d60041967779e763Marc Blank // If we find one, we do two things atomically: 1) set the body text for the 7114f15001bdfd11c79524b4e44d60041967779e763Marc Blank // message, and 2) mark the message loaded (i.e. completely loaded) 7124f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (id != null) { 7134f15001bdfd11c79524b4e44d60041967779e763Marc Blank userLog("Fetched body successfully for ", id); 7144f15001bdfd11c79524b4e44d60041967779e763Marc Blank mBindArgument[0] = id; 7154f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Body.CONTENT_URI) 7164f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withSelection(Body.MESSAGE_KEY + "=?", mBindArgument) 7174f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withValue(Body.TEXT_CONTENT, msg.mText) 7184f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 7194f15001bdfd11c79524b4e44d60041967779e763Marc Blank ops.add(ContentProviderOperation.newUpdate(Message.CONTENT_URI) 7204f15001bdfd11c79524b4e44d60041967779e763Marc Blank .withSelection(EmailContent.RECORD_ID + "=?", mBindArgument) 721068e073ffcb68e06785929208d6a6761b29030f3Marc Blank .withValue(Message.FLAG_LOADED, Message.FLAG_LOADED_COMPLETE) 7224f15001bdfd11c79524b4e44d60041967779e763Marc Blank .build()); 7234f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7244f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 7254f15001bdfd11c79524b4e44d60041967779e763Marc Blank 72677424af660458104b732bdcb718874b17d0cab3aMarc Blank for (Message msg: newEmails) { 72777424af660458104b732bdcb718874b17d0cab3aMarc Blank if (!msg.mFlagRead) { 72877424af660458104b732bdcb718874b17d0cab3aMarc Blank notifyCount++; 72977424af660458104b732bdcb718874b17d0cab3aMarc Blank } 73077424af660458104b732bdcb718874b17d0cab3aMarc Blank msg.addSaveOps(ops); 731ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 7324f15001bdfd11c79524b4e44d60041967779e763Marc Blank 733ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (Long id : deletedEmails) { 734ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newDelete( 735ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, id)).build()); 7361f19b92899b3589e5d6303a7dd35ebbf569e00b9Marc Blank AttachmentProvider.deleteAllAttachmentFiles(mContext, mAccount.mId, id); 737ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 7384f15001bdfd11c79524b4e44d60041967779e763Marc Blank 739ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!changedEmails.isEmpty()) { 740ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Server wins in a conflict... 741ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank for (ServerChange change : changedEmails) { 742ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank ContentValues cv = new ContentValues(); 743ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.read != null) { 744ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_READ, change.read); 745ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 746ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank if (change.flag != null) { 747ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank cv.put(MessageColumns.FLAG_FAVORITE, change.flag); 748ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank } 749ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 750ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ContentUris.withAppendedId(Message.CONTENT_URI, change.id)) 751ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .withValues(cv) 752ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank .build()); 753ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 754ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 7558d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank 7568d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank // We only want to update the sync key here 7578d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentValues mailboxValues = new ContentValues(); 7588d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank mailboxValues.put(Mailbox.SYNC_KEY, mMailbox.mSyncKey); 759ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank ops.add(ContentProviderOperation.newUpdate( 7608d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId)) 7618d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank .withValues(mailboxValues).build()); 762ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 7638480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank addCleanupOps(ops); 764147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank 7651b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // No commits if we're stopped 7661b275b9408d5b856e2482fa3951827489e9585ccMarc Blank synchronized (mService.getSynchronizer()) { 7671b275b9408d5b856e2482fa3951827489e9585ccMarc Blank if (mService.isStopped()) return; 7681b275b9408d5b856e2482fa3951827489e9585ccMarc Blank try { 7690a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank mContentResolver.applyBatch(EmailProvider.EMAIL_AUTHORITY, ops); 7700a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey); 7711b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } catch (RemoteException e) { 7721b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // There is nothing to be done here; fail by returning null 7731b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } catch (OperationApplicationException e) { 7741b275b9408d5b856e2482fa3951827489e9585ccMarc Blank // There is nothing to be done here; fail by returning null 7751b275b9408d5b856e2482fa3951827489e9585ccMarc Blank } 776ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 7778480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 77877424af660458104b732bdcb718874b17d0cab3aMarc Blank if (notifyCount > 0) { 779c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank // Use the new atomic add URI in EmailProvider 780c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank // We could add this to the operations being done, but it's not strictly 781c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank // speaking necessary, as the previous batch preserves the integrity of the 782c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank // database, whereas this is purely for notification purposes, and is itself atomic 783c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank ContentValues cv = new ContentValues(); 784c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank cv.put(EmailContent.FIELD_COLUMN_NAME, AccountColumns.NEW_MESSAGE_COUNT); 785c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank cv.put(EmailContent.ADD_COLUMN_NAME, notifyCount); 786c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank Uri uri = ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, mAccount.mId); 787c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank mContentResolver.update(uri, cv, null, null); 788a1e128b4676c1a4fb583b61ea94e561c6045e6f8Marc Blank MailService.actionNotifyNewMessages(mContext, mAccount.mId); 789894ec76e35053ab73ee9905b6737910e7fab7cd9Andrew Stadler } 79077424af660458104b732bdcb718874b17d0cab3aMarc Blank } 791ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 792ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 793ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank @Override 794ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank public String getCollectionName() { 795ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return "Email"; 796ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 797ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 7988480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank private void addCleanupOps(ArrayList<ContentProviderOperation> ops) { 7998480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // If we've sent local deletions, clear out the deleted table 8008480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mDeletedIdList) { 8018480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 8028480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.DELETED_CONTENT_URI, id)).build()); 8038480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8048480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // And same with the updates 8058480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank for (Long id: mUpdatedIdList) { 8068480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ops.add(ContentProviderOperation.newDelete( 8078480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI, id)).build()); 8088480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8098480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8108480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 8118480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank @Override 81248af7392c82262d17700e3fbdccf3a582809d449Marc Blank public void cleanup() { 8138480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank if (!mDeletedIdList.isEmpty() || !mUpdatedIdList.isEmpty()) { 8148480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>(); 8158480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank addCleanupOps(ops); 8168480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank try { 81748af7392c82262d17700e3fbdccf3a582809d449Marc Blank mContext.getContentResolver() 8188480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops); 8198480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } catch (RemoteException e) { 8208480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // There is nothing to be done here; fail by returning null 8218480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } catch (OperationApplicationException e) { 8228480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank // There is nothing to be done here; fail by returning null 8238480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8248480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8258480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank } 8268480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank 82736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank private String formatTwo(int num) { 82836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (num < 10) { 82936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return "0" + (char)('0' + num); 83036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else 83136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return Integer.toString(num); 83236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 83336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 83436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank /** 83536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * Create date/time in RFC8601 format. Oddly enough, for calendar date/time, Microsoft uses 83636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * a different format that excludes the punctuation (this is why I'm not putting this in a 83736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank * parent class) 83836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank */ 83936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank public String formatDateTime(Calendar calendar) { 84036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank StringBuilder sb = new StringBuilder(); 84136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank //YYYY-MM-DDTHH:MM:SS.MSSZ 84236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(calendar.get(Calendar.YEAR)); 84336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 84436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MONTH) + 1)); 84536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('-'); 84636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.DAY_OF_MONTH))); 84736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append('T'); 84836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.HOUR_OF_DAY))); 84936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 85036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.MINUTE))); 85136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(':'); 85236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(formatTwo(calendar.get(Calendar.SECOND))); 85336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank sb.append(".000Z"); 85436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank return sb.toString(); 85536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 85636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 857c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /** 858c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Note that messages in the deleted database preserve the message's unique id; therefore, we 859c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * can utilize this id to find references to the message. The only reference situation at this 860c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * point is in the Body table; it is when sending messages via SmartForward and SmartReply 861c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 862c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank private boolean messageReferenced(ContentResolver cr, long id) { 863c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArgument[0] = Long.toString(id); 864c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // See if this id is referenced in a body 865c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Body.CONTENT_URI, Body.ID_PROJECTION, WHERE_BODY_SOURCE_MESSAGE_KEY, 866c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank mBindArgument, null); 867c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank try { 868c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return c.moveToFirst(); 869c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } finally { 870c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank c.close(); 8715acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 872c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 873c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 874c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank /*private*/ /** 875c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * Serialize commands to delete items from the server; as we find items to delete, add their 876c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * id's to the deletedId's array 877c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * 878c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param s the Serializer we're using to create post data 879c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param deletedIds ids whose deletions are being sent to the server 880c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @param first whether or not this is the first command being sent 881c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @return true if SYNC_COMMANDS hasn't been sent (false otherwise) 882c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank * @throws IOException 883c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank */ 884c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean sendDeletedItems(Serializer s, ArrayList<Long> deletedIds, boolean first) 885c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank throws IOException { 886c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 8875acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank 888ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Find any of our deleted items 889ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor c = cr.query(Message.DELETED_CONTENT_URI, Message.LIST_PROJECTION, 890ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 891ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We keep track of the list of deleted item id's so that we can remove them from the 892ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // deleted table after the server receives our command 893c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.clear(); 894ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 895ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 8965acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = c.getString(Message.LIST_SERVER_ID_COLUMN); 8975acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 8985acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 8995acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 900c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Also check if this message is referenced elsewhere 901c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } else if (messageReferenced(cr, c.getLong(Message.CONTENT_ID_COLUMN))) { 902c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank userLog("Postponing deletion of referenced message: ", serverId); 903c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank continue; 9045acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } else if (first) { 9057c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 906ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank first = false; 907ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 908ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Send the command to delete this message 9095acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 910c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank deletedIds.add(c.getLong(Message.LIST_ID_COLUMN)); 911ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 912ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 913ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 914ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 915ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 916c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return first; 917c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 918c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 919c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank @Override 920c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank public boolean sendLocalChanges(Serializer s) throws IOException { 921c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank ContentResolver cr = mContext.getContentResolver(); 922c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 923c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // Never upsync from these folders 924c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (mMailbox.mType == Mailbox.TYPE_DRAFTS || mMailbox.mType == Mailbox.TYPE_OUTBOX) { 925c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank return false; 926c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank } 927c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 928c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank // This code is split out for unit testing purposes 929c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank boolean firstCommand = sendDeletedItems(s, mDeletedIdList, true); 930c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank 9314f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (!mFetchRequestList.isEmpty()) { 9324f15001bdfd11c79524b4e44d60041967779e763Marc Blank // Add FETCH commands for messages that need a body (i.e. we didn't find it during 9334f15001bdfd11c79524b4e44d60041967779e763Marc Blank // our earlier sync; this happens only in EAS 2.5 where the body couldn't be found 9344f15001bdfd11c79524b4e44d60041967779e763Marc Blank // after parsing the message's MIME data) 9354f15001bdfd11c79524b4e44d60041967779e763Marc Blank if (firstCommand) { 9364f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_COMMANDS); 9374f15001bdfd11c79524b4e44d60041967779e763Marc Blank firstCommand = false; 9384f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9394f15001bdfd11c79524b4e44d60041967779e763Marc Blank for (FetchRequest req: mFetchRequestList) { 9404f15001bdfd11c79524b4e44d60041967779e763Marc Blank s.start(Tags.SYNC_FETCH).data(Tags.SYNC_SERVER_ID, req.serverId).end(); 9414f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9424f15001bdfd11c79524b4e44d60041967779e763Marc Blank } 9434f15001bdfd11c79524b4e44d60041967779e763Marc Blank 94491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // Find our trash mailbox, since deletions will have been moved there... 94591e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank long trashMailboxId = 9460a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank Mailbox.findMailboxOfType(mContext, mMailbox.mAccountKey, Mailbox.TYPE_TRASH); 94791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 948ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Do the same now for updated items 949c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank Cursor c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION, 950ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null); 95191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 95291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // We keep track of the list of updated item id's as we did above with deleted items 953ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.clear(); 954ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 955ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank while (c.moveToNext()) { 956ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank long id = c.getLong(Message.LIST_ID_COLUMN); 957ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // Say we've handled this update 958ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank mUpdatedIdList.add(id); 959ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // We have the id of the changed item. But first, we have to find out its current 960ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // state, since the updated table saves the opriginal state 961ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank Cursor currentCursor = cr.query(ContentUris.withAppendedId(Message.CONTENT_URI, id), 962ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank UPDATES_PROJECTION, null, null, null); 963ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank try { 964ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank // If this item no longer exists (shouldn't be possible), just move along 965ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank if (!currentCursor.moveToFirst()) { 966ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 967ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 9685acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank // Keep going if there's no serverId 9695acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank String serverId = currentCursor.getString(UPDATES_SERVER_ID_COLUMN); 9705acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank if (serverId == null) { 9715acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank continue; 9725acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank } 97391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // If the message is now in the trash folder, it has been deleted by the user 97491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank if (currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN) == trashMailboxId) { 975c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (firstCommand) { 9767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 977c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank firstCommand = false; 97891e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank } 97991e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank // Send the command to delete this message 9805acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end(); 98191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank continue; 98291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank } 98391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank 98436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean flagChange = false; 98536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank boolean readChange = false; 98636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 9874471a6960d352242cc65bddf7888cc5335840c74Marc Blank long mailbox = currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN); 9884471a6960d352242cc65bddf7888cc5335840c74Marc Blank if (mailbox != c.getLong(Message.LIST_MAILBOX_KEY_COLUMN)) { 9894471a6960d352242cc65bddf7888cc5335840c74Marc Blank // The message has moved to another mailbox; add a request for this 9904471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Note: The Sync command doesn't handle moving messages, so we need 9914471a6960d352242cc65bddf7888cc5335840c74Marc Blank // to handle this as a "request" (similar to meeting response and 9924471a6960d352242cc65bddf7888cc5335840c74Marc Blank // attachment load) 9934471a6960d352242cc65bddf7888cc5335840c74Marc Blank mService.addRequest(new MessageMoveRequest(id, mailbox)); 9944471a6960d352242cc65bddf7888cc5335840c74Marc Blank // Regardless of other changes that might be made, we don't want to indicate 9954471a6960d352242cc65bddf7888cc5335840c74Marc Blank // that this message has been updated until the move request has been 9964471a6960d352242cc65bddf7888cc5335840c74Marc Blank // handled (without this, a crash between the flag upsync and the move 9974471a6960d352242cc65bddf7888cc5335840c74Marc Blank // would cause the move to be lost) 9984471a6960d352242cc65bddf7888cc5335840c74Marc Blank mUpdatedIdList.remove(id); 9994471a6960d352242cc65bddf7888cc5335840c74Marc Blank } 100036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 100136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // We can only send flag changes to the server in 12.0 or later 10024471a6960d352242cc65bddf7888cc5335840c74Marc Blank int flag = 0; 1003d5fadc87ea52aee033afd476369b29b29bfd434fMarc Blank if (mService.mProtocolVersionDouble >= Eas.SUPPORTED_PROTOCOL_EX2007_DOUBLE) { 100436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flag = currentCursor.getInt(UPDATES_FLAG_COLUMN); 100536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != c.getInt(Message.LIST_FAVORITE_COLUMN)) { 100636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank flagChange = true; 100736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 100836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 100936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 101091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank int read = currentCursor.getInt(UPDATES_READ_COLUMN); 101176eb7b252fc410f5f5d4e90ad54d4bde837de0aaMarc Blank if (read != c.getInt(Message.LIST_READ_COLUMN)) { 101236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank readChange = true; 101336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 101436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 101536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (!flagChange && !readChange) { 101636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // In this case, we've got nothing to send to the server 1017ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank continue; 1018ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 101936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank 1020c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (firstCommand) { 10217c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_COMMANDS); 1022c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank firstCommand = false; 1023ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 102436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Send the change to "read" and "favorite" (flagged) 10257c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.start(Tags.SYNC_CHANGE) 10267c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank .data(Tags.SYNC_SERVER_ID, c.getString(Message.LIST_SERVER_ID_COLUMN)) 102736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank .start(Tags.SYNC_APPLICATION_DATA); 102836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (readChange) { 102936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_READ, Integer.toString(read)); 103036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 103136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "Flag" is a relatively complex concept in EAS 12.0 and above. It is not only 103236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // the boolean "favorite" that we think of in Gmail, but it also represents a 103336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // follow up action, which can include a subject, start and due dates, and even 103436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // recurrences. We don't support any of this as yet, but EAS 12.0 and higher 103536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // require that a flag contain a status, a type, and four date fields, two each 103636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // for start date and end (due) date. 103736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flagChange) { 103836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank if (flag != 0) { 103936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Status 2 = set flag 104036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.start(Tags.EMAIL_FLAG).data(Tags.EMAIL_FLAG_STATUS, "2"); 104136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // "FollowUp" is the standard type 104236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.EMAIL_FLAG_TYPE, "FollowUp"); 104336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank long now = System.currentTimeMillis(); 104436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank Calendar calendar = 104536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT")); 104636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now); 104736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // Flags are required to have a start date and end date (duplicated) 104836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // First, we'll set the current date/time in GMT as the start time 104936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank String utc = formatDateTime(calendar); 105036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_START_DATE, utc).data(Tags.TASK_UTC_START_DATE, utc); 105136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank // And then we'll use one week from today for completion date 105236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank calendar.setTimeInMillis(now + 1*WEEKS); 105336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank utc = formatDateTime(calendar); 105436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.data(Tags.TASK_DUE_DATE, utc).data(Tags.TASK_UTC_DUE_DATE, utc); 105536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end(); 105636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } else { 105736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.tag(Tags.EMAIL_FLAG); 105836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 105936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank } 106036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank s.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE 1061ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1062ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank currentCursor.close(); 1063ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1064ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1065ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } finally { 1066ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank c.close(); 1067ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1068ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank 1069c6b98dad8e9002937d5755c61f2d8709807f4d22Marc Blank if (!firstCommand) { 10707c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank s.end(); // SYNC_COMMANDS 1071ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1072ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank return false; 1073ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank } 1074ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank} 1075