1d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank/*
2d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Copyright (C) 2011 The Android Open Source Project
3d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *
4d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Licensed under the Apache License, Version 2.0 (the "License");
5d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * you may not use this file except in compliance with the License.
6d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * You may obtain a copy of the License at
7d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *
8d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *      http://www.apache.org/licenses/LICENSE-2.0
9d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank *
10d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Unless required by applicable law or agreed to in writing, software
11d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * distributed under the License is distributed on an "AS IS" BASIS,
12d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * See the License for the specific language governing permissions and
14d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * limitations under the License.
15d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */
16d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
17d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankpackage com.android.exchange.adapter;
18d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
19d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentProviderOperation;
20d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.ContentValues;
21d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.Context;
22d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.content.OperationApplicationException;
23d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.os.RemoteException;
24d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport android.util.Log;
25d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
26d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.Logging;
27d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.provider.Account;
28d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.provider.EmailContent;
29d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.provider.EmailContent.Message;
30d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.provider.Mailbox;
31d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.service.EmailServiceStatus;
32d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.service.SearchParams;
33d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.emailcommon.utility.TextUtilities;
34d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.Eas;
35d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.EasResponse;
36d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.EasSyncService;
37d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.ExchangeService;
38d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.exchange.adapter.EmailSyncAdapter.EasEmailSyncParser;
39d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport com.android.mail.providers.UIProvider;
40d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
41d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport org.apache.http.HttpStatus;
42d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
43d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.io.IOException;
44d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.io.InputStream;
45d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankimport java.util.ArrayList;
46d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
47d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank/**
48d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank * Implementation of server-side search for EAS using the EmailService API
49d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank */
50d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blankpublic class Search {
51d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    // The shortest search query we'll accept
52d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    // TODO Check with UX whether this is correct
53d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int MIN_QUERY_LENGTH = 3;
54d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    // The largest number of results we'll ask for per server request
55d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    private static final int MAX_SEARCH_RESULTS = 100;
56d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
57d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    public static int searchMessages(Context context, long accountId, SearchParams searchParams,
58d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            long destMailboxId) {
59d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Sanity check for arguments
60d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        int offset = searchParams.mOffset;
61d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        int limit = searchParams.mLimit;
62d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        String filter = searchParams.mFilter;
63d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (limit < 0 || limit > MAX_SEARCH_RESULTS || offset < 0) return 0;
64d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // TODO Should this be checked in UI?  Are there guidelines for minimums?
65d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (filter == null || filter.length() < MIN_QUERY_LENGTH) return 0;
66d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
67d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        int res = 0;
68d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Account account = Account.restoreAccountWithId(context, accountId);
69d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (account == null) return res;
70d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        EasSyncService svc = EasSyncService.setupServiceForAccount(context, account);
71d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (svc == null) return res;
72d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        Mailbox searchMailbox = Mailbox.restoreMailboxWithId(context, destMailboxId);
73d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Sanity check; account might have been deleted?
74d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        if (searchMailbox == null) return res;
75d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        ContentValues statusValues = new ContentValues();
76d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        try {
77d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Set the status of this mailbox to indicate query
78edb48993842d47edaf75975258b3b3bf5a8ea48aVikram Aggarwal            statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.LIVE_QUERY);
79d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            searchMailbox.update(context, statusValues);
80d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
81d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            svc.mMailbox = searchMailbox;
82d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            svc.mAccount = account;
83d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Serializer s = new Serializer();
84d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.start(Tags.SEARCH_SEARCH).start(Tags.SEARCH_STORE);
85d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.SEARCH_NAME, "Mailbox");
86d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.start(Tags.SEARCH_QUERY).start(Tags.SEARCH_AND);
87d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.SYNC_CLASS, "Email");
88d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
89d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // If this isn't an inbox search, then include the collection id
90d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Mailbox inbox = Mailbox.restoreMailboxOfType(context, accountId, Mailbox.TYPE_INBOX);
91d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (inbox == null) return 0;
92d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (searchParams.mMailboxId != inbox.mId) {
93d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.data(Tags.SYNC_COLLECTION_ID, inbox.mServerId);
94d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
95d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
96d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.SEARCH_FREE_TEXT, filter);
97d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.end().end();              // SEARCH_AND, SEARCH_QUERY
98d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.start(Tags.SEARCH_OPTIONS);
99d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (offset == 0) {
100d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.tag(Tags.SEARCH_REBUILD_RESULTS);
101d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
102d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (searchParams.mIncludeChildren) {
103d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                s.tag(Tags.SEARCH_DEEP_TRAVERSAL);
104d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
105d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Range is sent in the form first-last (e.g. 0-9)
106d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.SEARCH_RANGE, offset + "-" + (offset + limit - 1));
107d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.start(Tags.BASE_BODY_PREFERENCE);
108d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML);
109d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.data(Tags.BASE_TRUNCATION_SIZE, "20000");
110d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.end();                    // BASE_BODY_PREFERENCE
111d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            s.end().end().end().done(); // SEARCH_OPTIONS, SEARCH_STORE, SEARCH_SEARCH
112d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            EasResponse resp = svc.sendHttpClientPost("Search", s.toByteArray());
113d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            try {
114d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                int code = resp.getStatus();
115d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (code == HttpStatus.SC_OK) {
116d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    InputStream is = resp.getInputStream();
117d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    try {
118d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        SearchParser sp = new SearchParser(is, svc, filter);
119d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        sp.parse();
120d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        res = sp.getTotalResults();
121d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    } finally {
122d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        is.close();
123d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
124d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else {
125d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    svc.userLog("Search returned " + code);
126d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
127d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } finally {
128d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                resp.close();
129d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
130d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        } catch (IOException e) {
131d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            svc.userLog("Search exception " + e);
132d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        } finally {
133d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            try {
134d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // TODO: Handle error states
135d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                // Set the status of this mailbox to indicate query over
136d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
137d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                searchMailbox.update(context, statusValues);
138d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ExchangeService.callback().syncMailboxStatus(destMailboxId,
139d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        EmailServiceStatus.SUCCESS, 100);
140d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } catch (RemoteException e) {
141d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
142d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
143d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        // Return the total count
144d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        return res;
145d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
146d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
147d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    /**
148d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     * Parse the result of a Search command
149d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank     */
150d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    static class SearchParser extends Parser {
151d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private final EasSyncService mService;
152d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private final String mQuery;
153d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private int mTotalResults;
154d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
155d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private SearchParser(InputStream in, EasSyncService service, String query)
156d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                throws IOException {
157d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            super(in);
158d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            mService = service;
159d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            mQuery = query;
160d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
161d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
162d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        protected int getTotalResults() {
163d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return mTotalResults;
164d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
165d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
166d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        @Override
167d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        public boolean parse() throws IOException {
168d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            boolean res = false;
169d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            if (nextTag(START_DOCUMENT) != Tags.SEARCH_SEARCH) {
170d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                throw new IOException();
171d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
172d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(START_DOCUMENT) != END_DOCUMENT) {
173d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (tag == Tags.SEARCH_STATUS) {
174d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    String status = getValue();
175d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (Eas.USER_LOG) {
176d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        Log.d(Logging.LOG_TAG, "Search status: " + status);
177d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
178d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SEARCH_RESPONSE) {
179d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    parseResponse();
180d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else {
181d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    skipTag();
182d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
183d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
184d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return res;
185d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
186d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
187d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private boolean parseResponse() throws IOException {
188d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            boolean res = false;
189d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SEARCH_RESPONSE) != END) {
190d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (tag == Tags.SEARCH_STORE) {
191d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    parseStore();
192d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else {
193d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    skipTag();
194d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
195d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
196d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return res;
197d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
198d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
199d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private boolean parseStore() throws IOException {
200d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            EmailSyncAdapter adapter = new EmailSyncAdapter(mService);
201d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            EasEmailSyncParser parser = adapter.new EasEmailSyncParser(this, adapter);
202d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            ArrayList<ContentProviderOperation> ops = new ArrayList<ContentProviderOperation>();
203d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            boolean res = false;
204d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
205d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SEARCH_STORE) != END) {
206d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (tag == Tags.SEARCH_STATUS) {
207b931f82fa44c2e26e2645c0d5fde9eef3e666efdPaul Westbrook                    String status = getValue();
208d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SEARCH_TOTAL) {
209d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    mTotalResults = getValueInt();
210d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SEARCH_RESULT) {
211d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    parseResult(parser, ops);
212d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else {
213d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    skipTag();
214d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
215d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
216d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
217d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            try {
218d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                adapter.mContentResolver.applyBatch(EmailContent.AUTHORITY, ops);
219d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (Eas.USER_LOG) {
220d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    mService.userLog("Saved " + ops.size() + " search results");
221d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
222d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } catch (RemoteException e) {
223d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                Log.d(Logging.LOG_TAG, "RemoteException while saving search results.");
224d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            } catch (OperationApplicationException e) {
225d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
226d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
227d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return res;
228d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
229d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank
230d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        private boolean parseResult(EasEmailSyncParser parser,
231d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                ArrayList<ContentProviderOperation> ops) throws IOException {
232d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            // Get an email sync parser for our incoming message data
233d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            boolean res = false;
234d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            Message msg = new Message();
235d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            while (nextTag(Tags.SEARCH_RESULT) != END) {
236d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                if (tag == Tags.SYNC_CLASS) {
237d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    getValue();
238d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SYNC_COLLECTION_ID) {
239d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    getValue();
240d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SEARCH_LONG_ID) {
241d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    msg.mProtocolSearchInfo = getValue();
242d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else if (tag == Tags.SEARCH_PROPERTIES) {
243d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    msg.mAccountKey = mService.mAccount.mId;
244d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    msg.mMailboxKey = mService.mMailbox.mId;
245d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    msg.mFlagLoaded = Message.FLAG_LOADED_COMPLETE;
246d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    parser.pushTag(tag);
247d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    parser.addData(msg, tag);
248d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    if (msg.mHtml != null) {
249d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                        msg.mHtml = TextUtilities.highlightTermsInHtml(msg.mHtml, mQuery);
250d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    }
251d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    msg.addSaveOps(ops);
252d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                } else {
253d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                    skipTag();
254d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank                }
255d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            }
256d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank            return res;
257d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank        }
258d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank    }
259d2e4d4675d04e78591ebd38fd084fc2cdbe144a0Marc Blank}
260