Home | History | Annotate | Download | only in eas
      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 
     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 
     50     public int getTotalResults() {
     51         return mTotalResults;
     52     }
     53 
     54     @Override
     55     protected String getCommand() {
     56         return "Search";
     57     }
     58 
     59     @Override
     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
    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