EmailSyncAdapter.java revision 8d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1
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
2067698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.mail.Address;
211f19b92899b3589e5d6303a7dd35ebbf569e00b9Marc Blankimport com.android.email.provider.AttachmentProvider;
22c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blankimport com.android.email.provider.EmailContent;
2300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.email.provider.EmailProvider;
24894ec76e35053ab73ee9905b6737910e7fab7cd9Andrew Stadlerimport com.android.email.provider.EmailContent.Account;
25c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blankimport com.android.email.provider.EmailContent.AccountColumns;
2667698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Attachment;
2767698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Mailbox;
2867698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.Message;
2967698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.MessageColumns;
3067698e240187c902bed123bf18d342ff25ec75c7Marc Blankimport com.android.email.provider.EmailContent.SyncColumns;
31a1e128b4676c1a4fb583b61ea94e561c6045e6f8Marc Blankimport com.android.email.service.MailService;
3200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.Eas;
3300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport com.android.exchange.EasSyncService;
34ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
35ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentProviderOperation;
36ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentResolver;
37ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentUris;
38ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentValues;
39ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.OperationApplicationException;
40ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.database.Cursor;
4177424af660458104b732bdcb718874b17d0cab3aMarc Blankimport android.net.Uri;
42ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.os.RemoteException;
4300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport android.webkit.MimeTypeMap;
44ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
4500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.IOException;
4600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.io.InputStream;
4700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.ArrayList;
4836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blankimport java.util.Calendar;
4900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.GregorianCalendar;
5000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blankimport java.util.TimeZone;
51ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
52ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/**
53ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Sync adapter for EAS email
54ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank *
55ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */
567c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blankpublic class EmailSyncAdapter extends AbstractSyncAdapter {
5700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank
5891e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank    private static final int UPDATES_READ_COLUMN = 0;
5991e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank    private static final int UPDATES_MAILBOX_KEY_COLUMN = 1;
6091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank    private static final int UPDATES_SERVER_ID_COLUMN = 2;
6136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank    private static final int UPDATES_FLAG_COLUMN = 3;
6291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank    private static final String[] UPDATES_PROJECTION =
6336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        {MessageColumns.FLAG_READ, MessageColumns.MAILBOX_KEY, SyncColumns.SERVER_ID,
6436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank            MessageColumns.FLAG_FAVORITE};
6591e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank
6691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank    String[] bindArguments = new String[2];
67ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
68ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
69ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
70ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
717c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank    public EmailSyncAdapter(Mailbox mailbox, EasSyncService service) {
72147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank        super(mailbox, service);
73ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
74ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
75ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
7648af7392c82262d17700e3fbdccf3a582809d449Marc Blank    public boolean parse(InputStream is) throws IOException {
7748af7392c82262d17700e3fbdccf3a582809d449Marc Blank        EasEmailSyncParser p = new EasEmailSyncParser(is, this);
78ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return p.parse();
79ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
80147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank
81368adeb5779fed5d64770d2131125dd93e43ab78Marc Blank    public class EasEmailSyncParser extends AbstractSyncParser {
82ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
83147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank        private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY =
84ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?";
85ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
86ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        private String mMailboxIdAsString;
87ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
8848af7392c82262d17700e3fbdccf3a582809d449Marc Blank        ArrayList<Message> newEmails = new ArrayList<Message>();
8948af7392c82262d17700e3fbdccf3a582809d449Marc Blank        ArrayList<Long> deletedEmails = new ArrayList<Long>();
9048af7392c82262d17700e3fbdccf3a582809d449Marc Blank        ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>();
9148af7392c82262d17700e3fbdccf3a582809d449Marc Blank
9248af7392c82262d17700e3fbdccf3a582809d449Marc Blank        public EasEmailSyncParser(InputStream in, EmailSyncAdapter adapter) throws IOException {
9348af7392c82262d17700e3fbdccf3a582809d449Marc Blank            super(in, adapter);
94ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mMailboxIdAsString = Long.toString(mMailbox.mId);
95ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
96ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
97147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank        @Override
98ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void wipe() {
99ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mContentResolver.delete(Message.CONTENT_URI,
100ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    Message.MAILBOX_KEY + "=" + mMailbox.mId, null);
101ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mContentResolver.delete(Message.DELETED_CONTENT_URI,
102ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    Message.MAILBOX_KEY + "=" + mMailbox.mId, null);
103ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mContentResolver.delete(Message.UPDATED_CONTENT_URI,
104ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    Message.MAILBOX_KEY + "=" + mMailbox.mId, null);
105ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
106ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
107ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void addData (Message msg) throws IOException {
108ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<Attachment> atts = new ArrayList<Attachment>();
109ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
1107c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            while (nextTag(Tags.SYNC_APPLICATION_DATA) != END) {
111ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
1127c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_ATTACHMENTS:
1135ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    case Tags.BASE_ATTACHMENTS: // BASE_ATTACHMENTS is used in EAS 12.0 and up
114d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        attachmentsParser(atts, msg);
115ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1167c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_TO:
11767698e240187c902bed123bf18d342ff25ec75c7Marc Blank                        msg.mTo = Address.pack(Address.parse(getValue()));
118ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1197c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_FROM:
120705a309bd78efd77469ac90a57849619de3317e3Mihai Preda                        Address[] froms = Address.parse(getValue());
121705a309bd78efd77469ac90a57849619de3317e3Mihai Preda                        if (froms != null && froms.length > 0) {
122705a309bd78efd77469ac90a57849619de3317e3Mihai Preda                          msg.mDisplayName = froms[0].toFriendly();
123ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
124705a309bd78efd77469ac90a57849619de3317e3Mihai Preda                        msg.mFrom = Address.pack(froms);
125ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1267c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_CC:
12767698e240187c902bed123bf18d342ff25ec75c7Marc Blank                        msg.mCc = Address.pack(Address.parse(getValue()));
128ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1297c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_REPLY_TO:
13067698e240187c902bed123bf18d342ff25ec75c7Marc Blank                        msg.mReplyTo = Address.pack(Address.parse(getValue()));
131ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1327c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_DATE_RECEIVED:
133ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String date = getValue();
134ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // 2009-02-11T18:03:03.627Z
135ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        GregorianCalendar cal = new GregorianCalendar();
136ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        cal.set(Integer.parseInt(date.substring(0, 4)), Integer.parseInt(date
137ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                .substring(5, 7)) - 1, Integer.parseInt(date.substring(8, 10)),
138ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                Integer.parseInt(date.substring(11, 13)), Integer.parseInt(date
139ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                        .substring(14, 16)), Integer.parseInt(date
140ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                                .substring(17, 19)));
141ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
142ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mTimeStamp = cal.getTimeInMillis();
143ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1447c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_SUBJECT:
145ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mSubject = getValue();
146ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1477c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_READ:
148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mFlagRead = getValueInt() == 1;
149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1507c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.BASE_BODY:
15100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                        bodyParser(msg);
15200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                        break;
1537c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_FLAG:
154ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                        msg.mFlagFavorite = flagParser();
155ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                        break;
1567c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_BODY:
15767698e240187c902bed123bf18d342ff25ec75c7Marc Blank                        String text = getValue();
15867698e240187c902bed123bf18d342ff25ec75c7Marc Blank                        msg.mText = text;
159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
161ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
164ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
165ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (atts.size() > 0) {
166ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                msg.mAttachments = atts;
167ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
168ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
16967698e240187c902bed123bf18d342ff25ec75c7Marc Blank
17000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        private void addParser(ArrayList<Message> emails) throws IOException {
171ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Message msg = new Message();
172ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mAccountKey = mAccount.mId;
173ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mMailboxKey = mMailbox.mId;
174f02459a766ddb1727d191daa0aeb559c8f848668Andrew Stadler            msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
1767c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            while (nextTag(Tags.SYNC_ADD) != END) {
177ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
1787c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.SYNC_SERVER_ID:
179ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mServerId = getValue();
180ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
1817c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.SYNC_APPLICATION_DATA:
182ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        addData(msg);
183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
184ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
185ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
186ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
187ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
188ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            emails.add(msg);
189ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
190ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
191ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank        // For now, we only care about the "active" state
192ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank        private Boolean flagParser() throws IOException {
193ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            Boolean state = false;
1947c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            while (nextTag(Tags.EMAIL_FLAG) != END) {
195ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                switch (tag) {
1967c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_FLAG_STATUS:
197ce17455fc5abf061e252d495288d0d56404b0b62Marc Blank                        state = getValueInt() == 2;
198ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                        break;
199ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                    default:
200ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                        skipTag();
201ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                }
202ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            }
203ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            return state;
204ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank        }
205ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank
20600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        private void bodyParser(Message msg) throws IOException {
20700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            String bodyType = Eas.BODY_PREFERENCE_TEXT;
20800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            String body = "";
2097c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            while (nextTag(Tags.EMAIL_BODY) != END) {
21000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                switch (tag) {
2117c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.BASE_TYPE:
21200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                        bodyType = getValue();
21300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                        break;
2147c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.BASE_DATA:
21500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                        body = getValue();
21600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                        break;
21700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                    default:
21800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                        skipTag();
21900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                }
22000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            }
22100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            // We always ask for TEXT or HTML; there's no third option
22200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            if (bodyType.equals(Eas.BODY_PREFERENCE_HTML)) {
22300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                msg.mHtml = body;
22400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            } else {
22500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                msg.mText = body;
22600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            }
22700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        }
22800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank
2295ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank        private void attachmentsParser(ArrayList<Attachment> atts, Message msg) throws IOException {
2305ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank            while (nextTag(Tags.EMAIL_ATTACHMENTS) != END) {
2315ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                switch (tag) {
2325ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    case Tags.EMAIL_ATTACHMENT:
2335ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    case Tags.BASE_ATTACHMENT:  // BASE_ATTACHMENT is used in EAS 12.0 and up
2345ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                        attachmentParser(atts, msg);
2355ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                        break;
2365ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    default:
2375ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                        skipTag();
2385ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                }
2395ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank            }
2405ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank        }
2415ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank
24200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        private void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException {
243ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String fileName = null;
244ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String length = null;
245d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank            String location = null;
246ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
2477c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            while (nextTag(Tags.EMAIL_ATTACHMENT) != END) {
248ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
2495ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    // We handle both EAS 2.5 and 12.0+ attachments here
2507c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_DISPLAY_NAME:
2515ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    case Tags.BASE_DISPLAY_NAME:
252ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        fileName = getValue();
253ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
2547c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_ATT_NAME:
2555ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    case Tags.BASE_FILE_REFERENCE:
256d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                        location = getValue();
257ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
2587c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_ATT_SIZE:
2595ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    case Tags.BASE_ESTIMATED_DATA_SIZE:
260ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        length = getValue();
261ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
262ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
263ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
264ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
265ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
266ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
2675ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank            if ((fileName != null) && (length != null) && (location != null)) {
268ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                Attachment att = new Attachment();
269ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                att.mEncoding = "base64";
270ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                att.mSize = Long.parseLong(length);
271ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                att.mFileName = fileName;
272d95115d72be6797835e1286fb8f0d2e5a01cf3a8Marc Blank                att.mLocation = location;
27300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                att.mMimeType = getMimeTypeFromFileName(fileName);
274ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                atts.add(att);
275ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                msg.mFlagAttachment = true;
276ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
277ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
278ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
27900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        /**
28000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank         * Try to determine a mime type from a file name, defaulting to application/x, where x
28100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank         * is either the extension or (if none) octet-stream
28200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank         * At the moment, this is somewhat lame, since many file types aren't recognized
28300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank         * @param fileName the file name to ponder
28400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank         * @return
28500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank         */
28600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        // Note: The MimeTypeMap method currently uses a very limited set of mime types
28700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        // A bug has been filed against this issue.
28800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        public String getMimeTypeFromFileName(String fileName) {
28900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            String mimeType;
29000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            int lastDot = fileName.lastIndexOf('.');
29100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            String extension = null;
2925ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank            if ((lastDot > 0) && (lastDot < fileName.length() - 1)) {
29319fd685351de80c62c9bc7f0f05fe96983a8078dMarc Blank                extension = fileName.substring(lastDot + 1).toLowerCase();
29400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            }
29500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            if (extension == null) {
29600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                // A reasonable default for now.
29700d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mimeType = "application/octet-stream";
29800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            } else {
29900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
30000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                if (mimeType == null) {
30100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                    mimeType = "application/" + extension;
30200d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank                }
30300d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            }
30400d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank            return mimeType;
30500d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        }
30600d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank
307ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        private Cursor getServerIdCursor(String serverId, String[] projection) {
308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            bindArguments[0] = serverId;
309ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            bindArguments[1] = mMailboxIdAsString;
310ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return mContentResolver.query(Message.CONTENT_URI, projection,
311ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    WHERE_SERVER_ID_AND_MAILBOX_KEY, bindArguments, null);
312ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
313ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
314048d45641b88c87172074aa5f29b3de307bc3712Marc Blank        private void deleteParser(ArrayList<Long> deletes, int entryTag) throws IOException {
315048d45641b88c87172074aa5f29b3de307bc3712Marc Blank            while (nextTag(entryTag) != END) {
316ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
3177c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.SYNC_SERVER_ID:
318ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String serverId = getValue();
319ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // Find the message in this mailbox with the given serverId
320ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        Cursor c = getServerIdCursor(serverId, Message.ID_COLUMN_PROJECTION);
321ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        try {
322ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            if (c.moveToFirst()) {
3230a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                                userLog("Deleting ", serverId);
324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                deletes.add(c.getLong(Message.ID_COLUMNS_ID_COLUMN));
325ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            }
326ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        } finally {
327ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            c.close();
328ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
336ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        class ServerChange {
337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            long id;
338ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            Boolean read;
339ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            Boolean flag;
340ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
341ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            ServerChange(long _id, Boolean _read, Boolean _flag) {
342ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                id = _id;
343ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                read = _read;
344ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                flag = _flag;
345ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
346ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
347ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
34800d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        private void changeParser(ArrayList<ServerChange> changes) throws IOException {
349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String serverId = null;
350ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            Boolean oldRead = false;
351ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            Boolean read = null;
352ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            Boolean oldFlag = false;
353ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank            Boolean flag = null;
354ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            long id = 0;
3557c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            while (nextTag(Tags.SYNC_CHANGE) != END) {
356ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
3577c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.SYNC_SERVER_ID:
358ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        serverId = getValue();
359ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        Cursor c = getServerIdCursor(serverId, Message.LIST_PROJECTION);
360ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        try {
361ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            if (c.moveToFirst()) {
3620a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                                userLog("Changing ", serverId);
363ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                oldRead = c.getInt(Message.LIST_READ_COLUMN) == Message.READ;
364ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                                oldFlag = c.getInt(Message.LIST_FAVORITE_COLUMN) == 1;
365ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                id = c.getLong(Message.LIST_ID_COLUMN);
366ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            }
367ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        } finally {
368ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            c.close();
369ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
370ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
3717c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_READ:
372ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        read = getValueInt() == 1;
373ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
3747c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.EMAIL_FLAG:
375ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                        flag = flagParser();
376ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                        break;
3777c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    case Tags.SYNC_APPLICATION_DATA:
378ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
379ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
380ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
381ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
382ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
3835ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank            if (((read != null) && !oldRead.equals(read)) ||
3845ba0321df8ea8d854de76263348ba26b0a8cff16Marc Blank                    ((flag != null) && !oldFlag.equals(flag))) {
385ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                changes.add(new ServerChange(id, read, flag));
386ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
387ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
388ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
38900d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank        /* (non-Javadoc)
39000d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank         * @see com.android.exchange.adapter.EasContentParser#commandsParser()
39100d91b2e12d65df06916afdc4bebca67fd27214cMarc Blank         */
392147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank        @Override
393ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void commandsParser() throws IOException {
3947c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            while (nextTag(Tags.SYNC_COMMANDS) != END) {
3957c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                if (tag == Tags.SYNC_ADD) {
396ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    addParser(newEmails);
3970a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                    incrementChangeCount();
398048d45641b88c87172074aa5f29b3de307bc3712Marc Blank                } else if (tag == Tags.SYNC_DELETE || tag == Tags.SYNC_SOFT_DELETE) {
399048d45641b88c87172074aa5f29b3de307bc3712Marc Blank                    deleteParser(deletedEmails, tag);
4000a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                    incrementChangeCount();
4017c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                } else if (tag == Tags.SYNC_CHANGE) {
402ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    changeParser(changedEmails);
4030a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                    incrementChangeCount();
404ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } else
405ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    skipTag();
406ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
40748af7392c82262d17700e3fbdccf3a582809d449Marc Blank        }
40848af7392c82262d17700e3fbdccf3a582809d449Marc Blank
40948af7392c82262d17700e3fbdccf3a582809d449Marc Blank        @Override
410a1e1f139046dfb28ea69c46d392f70764ad6f822Andrew Stadler        public void responsesParser() {
41148af7392c82262d17700e3fbdccf3a582809d449Marc Blank        }
41248af7392c82262d17700e3fbdccf3a582809d449Marc Blank
41348af7392c82262d17700e3fbdccf3a582809d449Marc Blank        @Override
414a1e1f139046dfb28ea69c46d392f70764ad6f822Andrew Stadler        public void commit() {
41548af7392c82262d17700e3fbdccf3a582809d449Marc Blank            int notifyCount = 0;
416ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
417ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Use a batch operation to handle the changes
418ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // TODO New mail notifications?  Who looks for these?
419ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
42077424af660458104b732bdcb718874b17d0cab3aMarc Blank            for (Message msg: newEmails) {
42177424af660458104b732bdcb718874b17d0cab3aMarc Blank                if (!msg.mFlagRead) {
42277424af660458104b732bdcb718874b17d0cab3aMarc Blank                    notifyCount++;
42377424af660458104b732bdcb718874b17d0cab3aMarc Blank                }
42477424af660458104b732bdcb718874b17d0cab3aMarc Blank                msg.addSaveOps(ops);
425ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
426ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            for (Long id : deletedEmails) {
427ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                ops.add(ContentProviderOperation.newDelete(
428ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        ContentUris.withAppendedId(Message.CONTENT_URI, id)).build());
4291f19b92899b3589e5d6303a7dd35ebbf569e00b9Marc Blank                AttachmentProvider.deleteAllAttachmentFiles(mContext, mAccount.mId, id);
430ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
431ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (!changedEmails.isEmpty()) {
432ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Server wins in a conflict...
433ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                for (ServerChange change : changedEmails) {
434ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                     ContentValues cv = new ContentValues();
435ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                    if (change.read != null) {
436ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                        cv.put(MessageColumns.FLAG_READ, change.read);
437ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                    }
438ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                    if (change.flag != null) {
439ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                        cv.put(MessageColumns.FLAG_FAVORITE, change.flag);
440ede29869a74740fd93bd74aa42d085d3704699cfMarc Blank                    }
441ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    ops.add(ContentProviderOperation.newUpdate(
442ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            ContentUris.withAppendedId(Message.CONTENT_URI, change.id))
443ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                .withValues(cv)
444ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                .build());
445ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
446ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
4478d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank
4488d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank            // We only want to update the sync key here
4498d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank            ContentValues mailboxValues = new ContentValues();
4508d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank            mailboxValues.put(Mailbox.SYNC_KEY, mMailbox.mSyncKey);
451ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ops.add(ContentProviderOperation.newUpdate(
4528d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                    ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId))
4538d12fd6f9d0993a8eaf7777d57ad04d80dfd2dd1Marc Blank                        .withValues(mailboxValues).build());
454ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
4558480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            addCleanupOps(ops);
456147e03d50b8a793d58d67917af4bc6333f8afac1Marc Blank
4571b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            // No commits if we're stopped
4581b275b9408d5b856e2482fa3951827489e9585ccMarc Blank            synchronized (mService.getSynchronizer()) {
4591b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                if (mService.isStopped()) return;
4601b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                try {
4610a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                    mContentResolver.applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
4620a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank                    userLog(mMailbox.mDisplayName, " SyncKey saved as: ", mMailbox.mSyncKey);
4631b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                } catch (RemoteException e) {
4641b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                    // There is nothing to be done here; fail by returning null
4651b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                } catch (OperationApplicationException e) {
4661b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                    // There is nothing to be done here; fail by returning null
4671b275b9408d5b856e2482fa3951827489e9585ccMarc Blank                }
468ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
4698480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank
47077424af660458104b732bdcb718874b17d0cab3aMarc Blank            if (notifyCount > 0) {
471c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                // Use the new atomic add URI in EmailProvider
472c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                // We could add this to the operations being done, but it's not strictly
473c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                // speaking necessary, as the previous batch preserves the integrity of the
474c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                // database, whereas this is purely for notification purposes, and is itself atomic
475c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                ContentValues cv = new ContentValues();
476c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                cv.put(EmailContent.FIELD_COLUMN_NAME, AccountColumns.NEW_MESSAGE_COUNT);
477c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                cv.put(EmailContent.ADD_COLUMN_NAME, notifyCount);
478c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                Uri uri = ContentUris.withAppendedId(Account.ADD_TO_FIELD_URI, mAccount.mId);
479c88a53832acb73bc6959cd544b2d6c7a2f52c367Marc Blank                mContentResolver.update(uri, cv, null, null);
480a1e128b4676c1a4fb583b61ea94e561c6045e6f8Marc Blank                MailService.actionNotifyNewMessages(mContext, mAccount.mId);
481894ec76e35053ab73ee9905b6737910e7fab7cd9Andrew Stadler            }
48277424af660458104b732bdcb718874b17d0cab3aMarc Blank        }
483ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
484ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
485ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
486ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String getCollectionName() {
487ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return "Email";
488ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
489ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
4908480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank    private void addCleanupOps(ArrayList<ContentProviderOperation> ops) {
4918480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank        // If we've sent local deletions, clear out the deleted table
4928480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank        for (Long id: mDeletedIdList) {
4938480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            ops.add(ContentProviderOperation.newDelete(
4948480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank                    ContentUris.withAppendedId(Message.DELETED_CONTENT_URI, id)).build());
4958480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank        }
4968480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank        // And same with the updates
4978480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank        for (Long id: mUpdatedIdList) {
4988480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            ops.add(ContentProviderOperation.newDelete(
4998480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank                    ContentUris.withAppendedId(Message.UPDATED_CONTENT_URI, id)).build());
5008480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank        }
5018480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank    }
5028480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank
5038480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank    @Override
50448af7392c82262d17700e3fbdccf3a582809d449Marc Blank    public void cleanup() {
5058480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank        if (!mDeletedIdList.isEmpty() || !mUpdatedIdList.isEmpty()) {
5068480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
5078480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            addCleanupOps(ops);
5088480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            try {
50948af7392c82262d17700e3fbdccf3a582809d449Marc Blank                mContext.getContentResolver()
5108480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank                    .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
5118480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            } catch (RemoteException e) {
5128480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank                // There is nothing to be done here; fail by returning null
5138480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            } catch (OperationApplicationException e) {
5148480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank                // There is nothing to be done here; fail by returning null
5158480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank            }
5168480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank        }
5178480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank    }
5188480f5bc2eb612920f7e17312a693b4d8c26f14bMarc Blank
51936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank    private String formatTwo(int num) {
52036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        if (num < 10) {
52136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank            return "0" + (char)('0' + num);
52236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        } else
52336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank            return Integer.toString(num);
52436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank    }
52536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank
52636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank    /**
52736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank     * Create date/time in RFC8601 format.  Oddly enough, for calendar date/time, Microsoft uses
52836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank     * a different format that excludes the punctuation (this is why I'm not putting this in a
52936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank     * parent class)
53036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank     */
53136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank    public String formatDateTime(Calendar calendar) {
53236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        StringBuilder sb = new StringBuilder();
53336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        //YYYY-MM-DDTHH:MM:SS.MSSZ
53436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(calendar.get(Calendar.YEAR));
53536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append('-');
53636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(formatTwo(calendar.get(Calendar.MONTH) + 1));
53736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append('-');
53836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(formatTwo(calendar.get(Calendar.DAY_OF_MONTH)));
53936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append('T');
54036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(formatTwo(calendar.get(Calendar.HOUR_OF_DAY)));
54136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(':');
54236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(formatTwo(calendar.get(Calendar.MINUTE)));
54336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(':');
54436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(formatTwo(calendar.get(Calendar.SECOND)));
54536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        sb.append(".000Z");
54636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank        return sb.toString();
54736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank    }
54836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank
549ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
55048af7392c82262d17700e3fbdccf3a582809d449Marc Blank    public boolean sendLocalChanges(Serializer s) throws IOException {
5510a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank        ContentResolver cr = mContext.getContentResolver();
552ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
5535acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank        // Never upsync from these folders
5545acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank        if (mMailbox.mType == Mailbox.TYPE_DRAFTS || mMailbox.mType == Mailbox.TYPE_OUTBOX) {
5555acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank            return false;
5565acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank        }
5575acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank
558ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // Find any of our deleted items
559ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        Cursor c = cr.query(Message.DELETED_CONTENT_URI, Message.LIST_PROJECTION,
560ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null);
561ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        boolean first = true;
562ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // We keep track of the list of deleted item id's so that we can remove them from the
563ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // deleted table after the server receives our command
564ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mDeletedIdList.clear();
565ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
566ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (c.moveToNext()) {
5675acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                String serverId = c.getString(Message.LIST_SERVER_ID_COLUMN);
5685acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                // Keep going if there's no serverId
5695acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                if (serverId == null) {
5705acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                    continue;
5715acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                } else if (first) {
5727c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    s.start(Tags.SYNC_COMMANDS);
573ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    first = false;
574ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
575ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Send the command to delete this message
5765acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end();
577ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mDeletedIdList.add(c.getLong(Message.LIST_ID_COLUMN));
578ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
579ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } finally {
580ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            c.close();
581ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
582ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
58391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank        // Find our trash mailbox, since deletions will have been moved there...
58491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank        long trashMailboxId =
5850a4d05f0d8753c67364f7167e62cea82aef9a81eMarc Blank            Mailbox.findMailboxOfType(mContext, mMailbox.mAccountKey, Mailbox.TYPE_TRASH);
58691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank
587ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // Do the same now for updated items
588ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION,
589ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null);
59091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank
59191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank        // We keep track of the list of updated item id's as we did above with deleted items
592ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mUpdatedIdList.clear();
593ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
594ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (c.moveToNext()) {
595ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                long id = c.getLong(Message.LIST_ID_COLUMN);
596ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Say we've handled this update
597ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mUpdatedIdList.add(id);
598ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // We have the id of the changed item.  But first, we have to find out its current
599ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // state, since the updated table saves the opriginal state
600ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                Cursor currentCursor = cr.query(ContentUris.withAppendedId(Message.CONTENT_URI, id),
601ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        UPDATES_PROJECTION, null, null, null);
602ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                try {
603ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // If this item no longer exists (shouldn't be possible), just move along
604ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (!currentCursor.moveToFirst()) {
605ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                         continue;
606ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
6075acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                    // Keep going if there's no serverId
6085acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                    String serverId = currentCursor.getString(UPDATES_SERVER_ID_COLUMN);
6095acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                    if (serverId == null) {
6105acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                        continue;
6115acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                    }
61291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                    // If the message is now in the trash folder, it has been deleted by the user
61391e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                    if (currentCursor.getLong(UPDATES_MAILBOX_KEY_COLUMN) == trashMailboxId) {
61491e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                         if (first) {
6157c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                            s.start(Tags.SYNC_COMMANDS);
61691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                            first = false;
61791e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                        }
61891e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                        // Send the command to delete this message
6195acdc9b59e5cd708bf568cded8ddfe3cacb23b73Marc Blank                        s.start(Tags.SYNC_DELETE).data(Tags.SYNC_SERVER_ID, serverId).end();
62091e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                        continue;
62191e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                    }
62291e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank
62336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    boolean flagChange = false;
62436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    boolean readChange = false;
62536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank
62636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    int flag = 0;
62736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank
62836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    // We can only send flag changes to the server in 12.0 or later
62936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    if (mService.mProtocolVersionDouble >= 12.0) {
63036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        flag = currentCursor.getInt(UPDATES_FLAG_COLUMN);
63136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        if (flag != c.getInt(Message.LIST_FAVORITE_COLUMN)) {
63236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            flagChange = true;
63336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        }
63436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    }
63536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank
63691e4233059a8b734dd67ffcfa0d08a0d4d8ab17dMarc Blank                    int read = currentCursor.getInt(UPDATES_READ_COLUMN);
63776eb7b252fc410f5f5d4e90ad54d4bde837de0aaMarc Blank                    if (read != c.getInt(Message.LIST_READ_COLUMN)) {
63836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        readChange = true;
63936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    }
64036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank
64136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    if (!flagChange && !readChange) {
64236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        // In this case, we've got nothing to send to the server
643ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        continue;
644ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
64536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank
646ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (first) {
6477c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                        s.start(Tags.SYNC_COMMANDS);
648ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        first = false;
649ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
65036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    // Send the change to "read" and "favorite" (flagged)
6517c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                    s.start(Tags.SYNC_CHANGE)
6527c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank                        .data(Tags.SYNC_SERVER_ID, c.getString(Message.LIST_SERVER_ID_COLUMN))
65336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        .start(Tags.SYNC_APPLICATION_DATA);
65436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    if (readChange) {
65536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        s.data(Tags.EMAIL_READ, Integer.toString(read));
65636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    }
65736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    // "Flag" is a relatively complex concept in EAS 12.0 and above.  It is not only
65836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    // the boolean "favorite" that we think of in Gmail, but it also represents a
65936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    // follow up action, which can include a subject, start and due dates, and even
66036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    // recurrences.  We don't support any of this as yet, but EAS 12.0 and higher
66136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    // require that a flag contain a status, a type, and four date fields, two each
66236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    // for start date and end (due) date.
66336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    if (flagChange) {
66436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        if (flag != 0) {
66536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            // Status 2 = set flag
66636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            s.start(Tags.EMAIL_FLAG).data(Tags.EMAIL_FLAG_STATUS, "2");
66736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            // "FollowUp" is the standard type
66836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            s.data(Tags.EMAIL_FLAG_TYPE, "FollowUp");
66936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            long now = System.currentTimeMillis();
67036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            Calendar calendar =
67136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                                GregorianCalendar.getInstance(TimeZone.getTimeZone("GMT"));
67236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            calendar.setTimeInMillis(now);
67336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            // Flags are required to have a start date and end date (duplicated)
67436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            // First, we'll set the current date/time in GMT as the start time
67536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            String utc = formatDateTime(calendar);
67636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            s.data(Tags.TASK_START_DATE, utc).data(Tags.TASK_UTC_START_DATE, utc);
67736e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            // And then we'll use one week from today for completion date
67836e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            calendar.setTimeInMillis(now + 1*WEEKS);
67936e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            utc = formatDateTime(calendar);
68036e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            s.data(Tags.TASK_DUE_DATE, utc).data(Tags.TASK_UTC_DUE_DATE, utc);
68136e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            s.end();
68236e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        } else {
68336e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                            s.tag(Tags.EMAIL_FLAG);
68436e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                        }
68536e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    }
68636e08ce9f808425ed573e182812f3a82ef4d5d45Marc Blank                    s.end().end(); // SYNC_APPLICATION_DATA, SYNC_CHANGE
687ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } finally {
688ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    currentCursor.close();
689ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
690ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
691ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } finally {
692ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            c.close();
693ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
694ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
695ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (!first) {
6967c582a7fb883b3be728f270fbe5277676fe37cf9Marc Blank            s.end(); // SYNC_COMMANDS
697ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
698ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return false;
699ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
700ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank}
701