• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package com.android.exchange.eas;
2 
3 import android.content.ContentValues;
4 import android.content.Context;
5 import android.content.SyncResult;
6 
7 import com.android.emailcommon.Logging;
8 import com.android.emailcommon.provider.Mailbox;
9 import com.android.emailcommon.service.SearchParams;
10 import com.android.exchange.CommandStatusException;
11 import com.android.exchange.Eas;
12 import com.android.exchange.EasResponse;
13 import com.android.exchange.adapter.Serializer;
14 import com.android.exchange.adapter.Tags;
15 import com.android.exchange.adapter.SearchParser;
16 import com.android.mail.providers.UIProvider;
17 import com.android.mail.utils.LogUtils;
18 
19 import org.apache.http.HttpEntity;
20 import org.apache.http.entity.ByteArrayEntity;
21 
22 import java.io.BufferedOutputStream;
23 import java.io.ByteArrayOutputStream;
24 import java.io.IOException;
25 import java.io.InputStream;
26 
27 public class EasSearch extends EasOperation {
28 
29     public final static int RESULT_NO_MESSAGES = 0;
30     public final static int RESULT_OK = 1;
31     public final static int RESULT_EMPTY_RESPONSE = 2;
32 
33     // The shortest search query we'll accept
34     // TODO Check with UX whether this is correct
35     private static final int MIN_QUERY_LENGTH = 3;
36     // The largest number of results we'll ask for per server request
37     private static final int MAX_SEARCH_RESULTS = 100;
38 
39     final SearchParams mSearchParams;
40     final long mDestMailboxId;
41     int mTotalResults;
42 
EasSearch(final Context context, final long accountId, final SearchParams searchParams, final long destMailboxId)43     public EasSearch(final Context context, final long accountId, final SearchParams searchParams,
44         final long destMailboxId) {
45         super(context, accountId);
46         mSearchParams = searchParams;
47         mDestMailboxId = destMailboxId;
48     }
49 
getTotalResults()50     public int getTotalResults() {
51         return mTotalResults;
52     }
53 
54     @Override
getCommand()55     protected String getCommand() {
56         return "Search";
57     }
58 
59     @Override
getRequestEntity()60     protected HttpEntity getRequestEntity() throws IOException {
61         // Sanity check for arguments
62         final int offset = mSearchParams.mOffset;
63         final int limit = mSearchParams.mLimit;
64         final String filter = mSearchParams.mFilter;
65         if (limit < 0 || limit > MAX_SEARCH_RESULTS || offset < 0) {
66             return null;
67         }
68         // TODO Should this be checked in UI?  Are there guidelines for minimums?
69         if (filter == null || filter.length() < MIN_QUERY_LENGTH) {
70             LogUtils.w(LOG_TAG, "filter too short");
71             return null;
72         }
73 
74         int res = 0;
75         final Mailbox searchMailbox = Mailbox.restoreMailboxWithId(mContext, mDestMailboxId);
76         // Sanity check; account might have been deleted?
77         if (searchMailbox == null) {
78             LogUtils.i(LOG_TAG, "search mailbox ceased to exist");
79             return null;
80         }
81         final ContentValues statusValues = new ContentValues(2);
82         try {
83             // Set the status of this mailbox to indicate query
84             statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.LIVE_QUERY);
85             searchMailbox.update(mContext, statusValues);
86 
87             final Serializer s = new Serializer();
88             s.start(Tags.SEARCH_SEARCH).start(Tags.SEARCH_STORE);
89             s.data(Tags.SEARCH_NAME, "Mailbox");
90             s.start(Tags.SEARCH_QUERY).start(Tags.SEARCH_AND);
91             s.data(Tags.SYNC_CLASS, "Email");
92 
93             // If this isn't an inbox search, then include the collection id
94             final Mailbox inbox =
95                     Mailbox.restoreMailboxOfType(mContext, mAccount.mId, Mailbox.TYPE_INBOX);
96             if (inbox == null) {
97                 LogUtils.i(LOG_TAG, "Inbox ceased to exist");
98                 return null;
99             }
100             if (mSearchParams.mMailboxId != inbox.mId) {
101                 s.data(Tags.SYNC_COLLECTION_ID, inbox.mServerId);
102             }
103             s.data(Tags.SEARCH_FREE_TEXT, filter);
104 
105             // Add the date window if appropriate
106             if (mSearchParams.mStartDate != null) {
107                 s.start(Tags.SEARCH_GREATER_THAN);
108                 s.tag(Tags.EMAIL_DATE_RECEIVED);
109                 s.data(Tags.SEARCH_VALUE, Eas.DATE_FORMAT.format(mSearchParams.mStartDate));
110                 s.end(); // SEARCH_GREATER_THAN
111             }
112             if (mSearchParams.mEndDate != null) {
113                 s.start(Tags.SEARCH_LESS_THAN);
114                 s.tag(Tags.EMAIL_DATE_RECEIVED);
115                 s.data(Tags.SEARCH_VALUE, Eas.DATE_FORMAT.format(mSearchParams.mEndDate));
116                 s.end(); // SEARCH_LESS_THAN
117             }
118             s.end().end(); // SEARCH_AND, SEARCH_QUERY
119             s.start(Tags.SEARCH_OPTIONS);
120             if (offset == 0) {
121                 s.tag(Tags.SEARCH_REBUILD_RESULTS);
122             }
123             if (mSearchParams.mIncludeChildren) {
124                 s.tag(Tags.SEARCH_DEEP_TRAVERSAL);
125             }
126             // Range is sent in the form first-last (e.g. 0-9)
127             s.data(Tags.SEARCH_RANGE, offset + "-" + (offset + limit - 1));
128             s.start(Tags.BASE_BODY_PREFERENCE);
129             s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML);
130             s.data(Tags.BASE_TRUNCATION_SIZE, "20000");
131             s.end();                    // BASE_BODY_PREFERENCE
132             s.end().end().end().done(); // SEARCH_OPTIONS, SEARCH_STORE, SEARCH_SEARCH
133             return makeEntity(s);
134         } catch (IOException e) {
135             LogUtils.d(LOG_TAG, e, "Search exception");
136         } finally {
137             // TODO: Handle error states
138             // Set the status of this mailbox to indicate query over
139             statusValues.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
140             statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
141             searchMailbox.update(mContext, statusValues);
142         }
143         LogUtils.i(LOG_TAG, "end returning null");
144         return null;
145     }
146 
147     @Override
handleResponse(final EasResponse response)148     protected int handleResponse(final EasResponse response)
149         throws IOException, CommandStatusException {
150         if (response.isEmpty()) {
151             return RESULT_EMPTY_RESPONSE;
152         }
153         final InputStream is = response.getInputStream();
154         try {
155             final Mailbox searchMailbox = Mailbox.restoreMailboxWithId(mContext, mDestMailboxId);
156             final SearchParser sp = new SearchParser(mContext, mContext.getContentResolver(),
157                     is, searchMailbox, mAccount, mSearchParams.mFilter);
158             sp.parse();
159             mTotalResults = sp.getTotalResults();
160         } finally {
161             is.close();
162         }
163         return RESULT_OK;
164     }
165 }
166