EmailSyncAdapter.java revision ab30d429e0c6069604aead9b5e6845b6b91b6a02
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
20ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport java.io.ByteArrayInputStream;
21ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport java.io.IOException;
22ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport java.io.InputStream;
23ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport java.util.ArrayList;
24ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport java.util.GregorianCalendar;
25ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport java.util.TimeZone;
26ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
27ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentProviderOperation;
28ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentResolver;
29ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentUris;
30ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.ContentValues;
31ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.content.OperationApplicationException;
32ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.database.Cursor;
33ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport android.os.RemoteException;
34ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
35ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.email.provider.EmailProvider;
36ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.exchange.EasSyncService;
37ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.exchange.EmailContent.Attachment;
38ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.exchange.EmailContent.Mailbox;
39ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.exchange.EmailContent.Message;
40ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.exchange.EmailContent.MessageColumns;
41ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankimport com.android.exchange.EmailContent.SyncColumns;
42ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
43ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank/**
44ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank * Sync adapter for EAS email
45ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank *
46ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank */
47ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blankpublic class EasEmailSyncAdapter extends EasSyncAdapter {
48ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
49ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    private static final String[] UPDATES_PROJECTION = {MessageColumns.FLAG_READ};
50ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
51ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    ArrayList<Long> mDeletedIdList = new ArrayList<Long>();
52ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
53ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    ArrayList<Long> mUpdatedIdList = new ArrayList<Long>();
54ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
55ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public EasEmailSyncAdapter(Mailbox mailbox) {
56ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        super(mailbox);
57ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
58ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
59ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
60ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public boolean parse(ByteArrayInputStream is, EasSyncService service) throws IOException {
61ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        EasEmailSyncParser p = new EasEmailSyncParser(is, service);
62ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return p.parse();
63ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
64ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
65ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    class EasEmailSyncParser extends EasContentParser {
66ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
67ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        private static final String WHERE_SERVER_ID_AND_MAILBOX_KEY =
68ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            SyncColumns.SERVER_ID + "=? and " + MessageColumns.MAILBOX_KEY + "=?";
69ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
70ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        private String mMailboxIdAsString;
71ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
72ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        String[] bindArguments = new String[2];
73ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
74ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public EasEmailSyncParser(InputStream in, EasSyncService service) throws IOException {
75ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            super(in, service);
76ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mMailboxIdAsString = Long.toString(mMailbox.mId);
77ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            //setDebug(true); // DON'T CHECK IN WITH THIS
78ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
79ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
80ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void wipe() {
81ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mContentResolver.delete(Message.CONTENT_URI,
82ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    Message.MAILBOX_KEY + "=" + mMailbox.mId, null);
83ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mContentResolver.delete(Message.DELETED_CONTENT_URI,
84ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    Message.MAILBOX_KEY + "=" + mMailbox.mId, null);
85ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mContentResolver.delete(Message.UPDATED_CONTENT_URI,
86ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    Message.MAILBOX_KEY + "=" + mMailbox.mId, null);
87ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
88ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
89ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void addData (Message msg) throws IOException {
90ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String to = "";
91ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String from = "";
92ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String cc = "";
93ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String replyTo = "";
94ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            int size = 0;
95ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
96ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<Attachment> atts = new ArrayList<Attachment>();
97ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
98ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (nextTag(EasTags.SYNC_APPLICATION_DATA) != END) {
99ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
100ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_ATTACHMENTS:
101ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
102ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_ATTACHMENT:
103ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        attachmentParser(atts, msg);
104ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
105ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_TO:
106ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        to = getValue();
107ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
108ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_FROM:
109ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        from = getValue();
110ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String sender = from;
111ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        int q = from.indexOf('\"');
112ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        if (q >= 0) {
113ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            int qq = from.indexOf('\"', q + 1);
114ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            if (qq > 0) {
115ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                sender = from.substring(q + 1, qq);
116ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            }
117ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
118ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mDisplayName = sender;
119ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
120ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_CC:
121ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        cc = getValue();
122ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
123ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_REPLY_TO:
124ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        replyTo = getValue();
125ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
126ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_DATE_RECEIVED:
127ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String date = getValue();
128ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // 2009-02-11T18:03:03.627Z
129ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        GregorianCalendar cal = new GregorianCalendar();
130ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        cal.set(Integer.parseInt(date.substring(0, 4)), Integer.parseInt(date
131ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                .substring(5, 7)) - 1, Integer.parseInt(date.substring(8, 10)),
132ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                Integer.parseInt(date.substring(11, 13)), Integer.parseInt(date
133ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                        .substring(14, 16)), Integer.parseInt(date
134ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                                .substring(17, 19)));
135ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        cal.setTimeZone(TimeZone.getTimeZone("GMT"));
136ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mTimeStamp = cal.getTimeInMillis();
137ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
138ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_SUBJECT:
139ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mSubject = getValue();
140ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
141ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_READ:
142ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mFlagRead = getValueInt() == 1;
143ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
144ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_BODY:
145ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mTextInfo = "X;X;8;" + size; // location;encoding;charset;size
146ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mText = getValue();
147ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // For now...
148ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mPreview = "Fake preview"; // Messages.previewFromText(body);
149ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
150ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
151ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
152ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
153ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
154ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
155ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mTo = to;
156ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mFrom = from;
157ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mCc = cc;
158ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mReplyTo = replyTo;
159ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (atts.size() > 0) {
160ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                msg.mAttachments = atts;
161ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
162ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
163ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
164ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
165ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void addParser(ArrayList<Message> emails) throws IOException {
166ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            Message msg = new Message();
167ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mAccountKey = mAccount.mId;
168ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mMailboxKey = mMailbox.mId;
169ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mFlagLoaded = Message.LOADED;
170ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
171ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (nextTag(EasTags.SYNC_ADD) != END) {
172ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
173ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.SYNC_SERVER_ID:
174ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        msg.mServerId = getValue();
175ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
176ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.SYNC_APPLICATION_DATA:
177ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        addData(msg);
178ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
179ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
180ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
181ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
182ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
183ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
184ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Tell the provider that this is synced back
185ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            msg.mServerVersion = mMailbox.mSyncKey;
186ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            emails.add(msg);
187ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
188ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
189ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void attachmentParser(ArrayList<Attachment> atts, Message msg) throws IOException {
190ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String fileName = null;
191ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String length = null;
192ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String lvl = null;
193ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
194ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (nextTag(EasTags.EMAIL_ATTACHMENT) != END) {
195ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
196ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_DISPLAY_NAME:
197ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        fileName = getValue();
198ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
199ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_ATT_NAME:
200ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        lvl = getValue();
201ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
202ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_ATT_SIZE:
203ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        length = getValue();
204ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
205ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
206ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
207ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
208ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
209ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
210ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (fileName != null && length != null && lvl != null) {
211ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                Attachment att = new Attachment();
212ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                att.mEncoding = "base64";
213ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                att.mSize = Long.parseLong(length);
214ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                att.mFileName = fileName;
215ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                atts.add(att);
216ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                msg.mFlagAttachment = true;
217ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
218ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
219ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
220ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        private Cursor getServerIdCursor(String serverId, String[] projection) {
221ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            bindArguments[0] = serverId;
222ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            bindArguments[1] = mMailboxIdAsString;
223ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            return mContentResolver.query(Message.CONTENT_URI, projection,
224ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    WHERE_SERVER_ID_AND_MAILBOX_KEY, bindArguments, null);
225ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
226ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
227ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void deleteParser(ArrayList<Long> deletes) throws IOException {
228ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (nextTag(EasTags.SYNC_DELETE) != END) {
229ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
230ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.SYNC_SERVER_ID:
231ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        String serverId = getValue();
232ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // Find the message in this mailbox with the given serverId
233ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        Cursor c = getServerIdCursor(serverId, Message.ID_COLUMN_PROJECTION);
234ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        try {
235ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            if (c.moveToFirst()) {
236ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                mService.userLog("Deleting " + serverId);
237ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                deletes.add(c.getLong(Message.ID_COLUMNS_ID_COLUMN));
238ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            }
239ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        } finally {
240ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            c.close();
241ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
242ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
243ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
244ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
245ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
246ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
247ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
248ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
249ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        class ServerChange {
250ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            long id;
251ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            boolean read;
252ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
253ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ServerChange(long _id, boolean _read) {
254ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                id = _id;
255ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                read = _read;
256ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
257ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
258ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
259ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void changeParser(ArrayList<ServerChange> changes) throws IOException {
260ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            String serverId = null;
261ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            boolean oldRead = false;
262ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            boolean read = true;
263ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            long id = 0;
264ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (nextTag(EasTags.SYNC_CHANGE) != END) {
265ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                switch (tag) {
266ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.SYNC_SERVER_ID:
267ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        serverId = getValue();
268ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        Cursor c = getServerIdCursor(serverId, Message.LIST_PROJECTION);
269ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        try {
270ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            if (c.moveToFirst()) {
271ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                mService.userLog("Changing " + serverId);
272ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                oldRead = c.getInt(Message.LIST_READ_COLUMN) == Message.READ;
273ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                id = c.getLong(Message.LIST_ID_COLUMN);
274ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            }
275ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        } finally {
276ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            c.close();
277ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        }
278ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
279ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.EMAIL_READ:
280ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        read = getValueInt() == 1;
281ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
282ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    case EasTags.SYNC_APPLICATION_DATA:
283ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        break;
284ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    default:
285ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        skipTag();
286ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
287ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
288ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (oldRead != read) {
289ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                changes.add(new ServerChange(id, read));
290ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
291ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
292ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
293ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        public void commandsParser() throws IOException {
294ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<Message> newEmails = new ArrayList<Message>();
295ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<Long> deletedEmails = new ArrayList<Long>();
296ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<ServerChange> changedEmails = new ArrayList<ServerChange>();
297ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
298ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (nextTag(EasTags.SYNC_COMMANDS) != END) {
299ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (tag == EasTags.SYNC_ADD) {
300ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    addParser(newEmails);
301ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } else if (tag == EasTags.SYNC_DELETE) {
302ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    deleteParser(deletedEmails);
303ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } else if (tag == EasTags.SYNC_CHANGE) {
304ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    changeParser(changedEmails);
305ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } else
306ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    skipTag();
307ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
308ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
309ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // Use a batch operation to handle the changes
310ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // TODO New mail notifications?  Who looks for these?
311ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
312ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            for (Message content : newEmails) {
313ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                content.addSaveOps(ops);
314ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
315ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            for (Long id : deletedEmails) {
316ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                ops.add(ContentProviderOperation.newDelete(
317ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        ContentUris.withAppendedId(Message.CONTENT_URI, id)).build());
318ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
319ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            if (!changedEmails.isEmpty()) {
320ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Server wins in a conflict...
321ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                for (ServerChange change : changedEmails) {
322ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // For now, don't handle read->unread
323ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    ContentValues cv = new ContentValues();
324ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    cv.put(MessageColumns.FLAG_READ, change.read);
325ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    ops.add(ContentProviderOperation.newUpdate(
326ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                            ContentUris.withAppendedId(Message.CONTENT_URI, change.id))
327ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                .withValues(cv)
328ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                                .build());
329ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
330ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
331ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            ops.add(ContentProviderOperation.newUpdate(
332ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    ContentUris.withAppendedId(Mailbox.CONTENT_URI, mMailbox.mId)).withValues(
333ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    mMailbox.toContentValues()).build());
334ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
335ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            // If we've sent local deletions, clear out the deleted table
336ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            for (Long id: mDeletedIdList) {
337ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                ops.add(ContentProviderOperation.newDelete(
338ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        ContentUris.withAppendedId(Message.DELETED_CONTENT_URI, id)).build());
339ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
340ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
341ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            try {
342ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mService.mContext.getContentResolver()
343ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        .applyBatch(EmailProvider.EMAIL_AUTHORITY, ops);
344ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } catch (RemoteException e) {
345ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // There is nothing to be done here; fail by returning null
346ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            } catch (OperationApplicationException e) {
347ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // There is nothing to be done here; fail by returning null
348ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
349ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
350ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            mService.userLog("SyncKey confirmed as: " + mMailbox.mSyncKey);
351ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
352ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
353ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
354ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
355ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public String getCollectionName() {
356ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return "Email";
357ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
358ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
359ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    @Override
360ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    public boolean sendLocalChanges(EasSerializer s, EasSyncService service) throws IOException {
361ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        ContentResolver cr = service.mContext.getContentResolver();
362ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
363ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // Find any of our deleted items
364ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        Cursor c = cr.query(Message.DELETED_CONTENT_URI, Message.LIST_PROJECTION,
365ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null);
366ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        boolean first = true;
367ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // We keep track of the list of deleted item id's so that we can remove them from the
368ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // deleted table after the server receives our command
369ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mDeletedIdList.clear();
370ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
371ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (c.moveToNext()) {
372ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                if (first) {
373ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    s.start("Commands");
374ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    first = false;
375ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
376ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Send the command to delete this message
377ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                s.start("Delete")
378ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    .data("ServerId", c.getString(Message.LIST_SERVER_ID_COLUMN))
379ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    .end("Delete");
380ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mDeletedIdList.add(c.getLong(Message.LIST_ID_COLUMN));
381ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
382ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } finally {
383ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            c.close();
384ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
385ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
386ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // Do the same now for updated items
387ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        c = cr.query(Message.UPDATED_CONTENT_URI, Message.LIST_PROJECTION,
388ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                MessageColumns.MAILBOX_KEY + '=' + mMailbox.mId, null, null);
389ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // We keep track of the list of updated item id's so that we can remove them from the
390ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        // deleted table after the server receives our command
391ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        mUpdatedIdList.clear();
392ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        try {
393ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            while (c.moveToNext()) {
394ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                long id = c.getLong(Message.LIST_ID_COLUMN);
395ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // Say we've handled this update
396ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                mUpdatedIdList.add(id);
397ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // We have the id of the changed item.  But first, we have to find out its current
398ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                // state, since the updated table saves the opriginal state
399ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                Cursor currentCursor = cr.query(ContentUris.withAppendedId(Message.CONTENT_URI, id),
400ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        UPDATES_PROJECTION, null, null, null);
401ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                try {
402ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // If this item no longer exists (shouldn't be possible), just move along
403ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (!currentCursor.moveToFirst()) {
404ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                         continue;
405ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
406ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    int read = currentCursor.getInt(0);
407ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (read == c.getInt(Message.LIST_READ_COLUMN)) {
408ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        // The read state hasn't really changed, so move on...
409ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        continue;
410ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
411ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    if (first) {
412ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        s.start("Commands");
413ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        first = false;
414ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    }
415ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // Send the change to "read".  We'll do "flagged" here eventually as well
416ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    // TODO Add support for flags here (EAS 12.0 and above)
417ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    s.start("Change")
418ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        .data("ServerId", c.getString(Message.LIST_SERVER_ID_COLUMN))
419ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        .start("ApplicationData")
420ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        .data("Read", Integer.toString(read))
421ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        .end("ApplicationData")
422ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                        .end("Change");
423ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                } finally {
424ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                    currentCursor.close();
425ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank                }
426ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            }
427ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        } finally {
428ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            c.close();
429ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
430ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank
431ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        if (!first) {
432ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank            s.end("Commands");
433ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        }
434ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank        return false;
435ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank    }
436ab30d429e0c6069604aead9b5e6845b6b91b6a02Marc Blank}
437