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