blob: 64100f413fff2929883368ffed9c397663318a46 [file] [log] [blame]
package com.android.exchange.eas;
import android.content.ContentValues;
import android.content.Context;
import android.content.SyncResult;
import com.android.emailcommon.Logging;
import com.android.emailcommon.provider.Account;
import com.android.emailcommon.provider.Mailbox;
import com.android.emailcommon.service.SearchParams;
import com.android.exchange.CommandStatusException;
import com.android.exchange.Eas;
import com.android.exchange.EasResponse;
import com.android.exchange.adapter.Serializer;
import com.android.exchange.adapter.Tags;
import com.android.exchange.adapter.SearchParser;
import com.android.mail.providers.UIProvider;
import com.android.mail.utils.LogUtils;
import org.apache.http.HttpEntity;
import org.apache.http.entity.ByteArrayEntity;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
public class EasSearch extends EasOperation {
public final static int RESULT_NO_MESSAGES = 0;
public final static int RESULT_OK = 1;
public final static int RESULT_EMPTY_RESPONSE = 2;
// The shortest search query we'll accept
// TODO Check with UX whether this is correct
private static final int MIN_QUERY_LENGTH = 3;
// The largest number of results we'll ask for per server request
private static final int MAX_SEARCH_RESULTS = 100;
final SearchParams mSearchParams;
final long mDestMailboxId;
int mTotalResults;
Mailbox mSearchMailbox;
public EasSearch(final Context context, final Account account, final SearchParams searchParams,
final long destMailboxId) {
super(context, account);
mSearchParams = searchParams;
mDestMailboxId = destMailboxId;
}
public int getTotalResults() {
return mTotalResults;
}
@Override
protected String getCommand() {
return "Search";
}
@Override
protected HttpEntity getRequestEntity() throws IOException {
// Sanity check for arguments
final int offset = mSearchParams.mOffset;
final int limit = mSearchParams.mLimit;
final String filter = mSearchParams.mFilter;
if (limit < 0 || limit > MAX_SEARCH_RESULTS || offset < 0) {
return null;
}
// TODO Should this be checked in UI? Are there guidelines for minimums?
if (filter == null || filter.length() < MIN_QUERY_LENGTH) {
LogUtils.w(LOG_TAG, "filter too short");
return null;
}
int res = 0;
mSearchMailbox = Mailbox.restoreMailboxWithId(mContext, mDestMailboxId);
// Sanity check; account might have been deleted?
if (mSearchMailbox == null) {
LogUtils.i(LOG_TAG, "search mailbox ceased to exist");
return null;
}
try {
// Set the status of this mailbox to indicate query
final ContentValues statusValues = new ContentValues(1);
statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.LIVE_QUERY);
mSearchMailbox.update(mContext, statusValues);
final Serializer s = new Serializer();
s.start(Tags.SEARCH_SEARCH).start(Tags.SEARCH_STORE);
s.data(Tags.SEARCH_NAME, "Mailbox");
s.start(Tags.SEARCH_QUERY).start(Tags.SEARCH_AND);
s.data(Tags.SYNC_CLASS, "Email");
// If this isn't an inbox search, then include the collection id
final Mailbox inbox =
Mailbox.restoreMailboxOfType(mContext, mAccount.mId, Mailbox.TYPE_INBOX);
if (inbox == null) {
LogUtils.i(LOG_TAG, "Inbox ceased to exist");
return null;
}
if (mSearchParams.mMailboxId != inbox.mId) {
s.data(Tags.SYNC_COLLECTION_ID, inbox.mServerId);
}
s.data(Tags.SEARCH_FREE_TEXT, filter);
// Add the date window if appropriate
if (mSearchParams.mStartDate != null) {
s.start(Tags.SEARCH_GREATER_THAN);
s.tag(Tags.EMAIL_DATE_RECEIVED);
s.data(Tags.SEARCH_VALUE, Eas.DATE_FORMAT.format(mSearchParams.mStartDate));
s.end(); // SEARCH_GREATER_THAN
}
if (mSearchParams.mEndDate != null) {
s.start(Tags.SEARCH_LESS_THAN);
s.tag(Tags.EMAIL_DATE_RECEIVED);
s.data(Tags.SEARCH_VALUE, Eas.DATE_FORMAT.format(mSearchParams.mEndDate));
s.end(); // SEARCH_LESS_THAN
}
s.end().end(); // SEARCH_AND, SEARCH_QUERY
s.start(Tags.SEARCH_OPTIONS);
if (offset == 0) {
s.tag(Tags.SEARCH_REBUILD_RESULTS);
}
if (mSearchParams.mIncludeChildren) {
s.tag(Tags.SEARCH_DEEP_TRAVERSAL);
}
// Range is sent in the form first-last (e.g. 0-9)
s.data(Tags.SEARCH_RANGE, offset + "-" + (offset + limit - 1));
s.start(Tags.BASE_BODY_PREFERENCE);
s.data(Tags.BASE_TYPE, Eas.BODY_PREFERENCE_HTML);
s.data(Tags.BASE_TRUNCATION_SIZE, "20000");
s.end(); // BASE_BODY_PREFERENCE
s.end().end().end().done(); // SEARCH_OPTIONS, SEARCH_STORE, SEARCH_SEARCH
return makeEntity(s);
} catch (IOException e) {
LogUtils.d(LOG_TAG, e, "Search exception");
}
LogUtils.i(LOG_TAG, "end returning null");
return null;
}
@Override
protected int handleResponse(final EasResponse response)
throws IOException, CommandStatusException {
if (response.isEmpty()) {
return RESULT_EMPTY_RESPONSE;
}
final InputStream is = response.getInputStream();
try {
final Mailbox searchMailbox = Mailbox.restoreMailboxWithId(mContext, mDestMailboxId);
final SearchParser sp = new SearchParser(mContext, mContext.getContentResolver(),
is, searchMailbox, mAccount, mSearchParams.mFilter);
sp.parse();
mTotalResults = sp.getTotalResults();
} finally {
is.close();
}
return RESULT_OK;
}
protected void onRequestComplete() {
if (mSearchMailbox != null) {
// TODO: Handle error states
final ContentValues statusValues = new ContentValues(2);
statusValues.put(Mailbox.UI_SYNC_STATUS, UIProvider.SyncStatus.NO_SYNC);
statusValues.put(Mailbox.SYNC_TIME, System.currentTimeMillis());
mSearchMailbox.update(mContext, statusValues);
}
}
}