Merge "some brain dead typing shortening"
diff --git a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
index 9e292ce..589a3a0 100644
--- a/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
+++ b/emailcommon/src/com/android/emailcommon/provider/Mailbox.java
@@ -390,7 +390,6 @@
         }
         switch (getMailboxType(context, mailboxId)) {
             case -1: // not found
-            case TYPE_SEARCH:
             case TYPE_DRAFTS:
             case TYPE_OUTBOX:
                 return false;
diff --git a/emailcommon/src/com/android/emailcommon/service/SearchParams.java b/emailcommon/src/com/android/emailcommon/service/SearchParams.java
index 1107aca..9c4ab2e 100644
--- a/emailcommon/src/com/android/emailcommon/service/SearchParams.java
+++ b/emailcommon/src/com/android/emailcommon/service/SearchParams.java
@@ -16,15 +16,15 @@
 
 package com.android.emailcommon.service;
 
-import com.android.emailcommon.provider.Mailbox;
-
 import android.os.Parcel;
 import android.os.Parcelable;
 
+import com.android.emailcommon.provider.Mailbox;
+
 public class SearchParams implements Parcelable {
     public static final long ALL_MAILBOXES = Mailbox.NO_MAILBOX;
 
-    private static final int DEFAULT_LIMIT = 20;
+    private static final int DEFAULT_LIMIT = 10; // Need input on what this number should be
     private static final int DEFAULT_OFFSET = 0;
 
     // The id of the mailbox to be searched; if -1, all mailboxes MUST be searched
diff --git a/src/com/android/email/Controller.java b/src/com/android/email/Controller.java
index 2069481..0023469 100644
--- a/src/com/android/email/Controller.java
+++ b/src/com/android/email/Controller.java
@@ -62,6 +62,7 @@
 import java.io.InputStream;
 import java.util.ArrayList;
 import java.util.Collection;
+import java.util.HashMap;
 import java.util.HashSet;
 import java.util.concurrent.ConcurrentHashMap;
 
@@ -392,8 +393,7 @@
 
         IEmailService service = getServiceForAccount(accountId);
         if (service != null) {
-            // Service implementation
-            try {
+           try {
                 service.startSync(mailboxId, userRequest);
             } catch (RemoteException e) {
                 // TODO Change exception handling to be consistent with however this method
@@ -409,7 +409,8 @@
                         Account.restoreAccountWithId(mProviderContext, accountId);
                     Mailbox mailbox =
                         Mailbox.restoreMailboxWithId(mProviderContext, mailboxId);
-                    if (account == null || mailbox == null) {
+                    if (account == null || mailbox == null ||
+                            mailbox.mType == Mailbox.TYPE_SEARCH) {
                         return;
                     }
                     mLegacyController.synchronizeMailbox(account, mailbox, mLegacyListener);
@@ -669,12 +670,20 @@
      * @param mailboxId the mailbox
      */
     public void loadMoreMessages(final long mailboxId) {
-        Utility.runAsync(new Runnable() {
+        EmailAsyncTask.runAsyncParallel(new Runnable() {
             public void run() {
                 Mailbox mailbox = Mailbox.restoreMailboxWithId(mProviderContext, mailboxId);
                 if (mailbox == null) {
                     return;
                 }
+                if (mailbox.mType == Mailbox.TYPE_SEARCH) {
+                    try {
+                        searchMore(mailbox.mAccountKey);
+                    } catch (MessagingException e) {
+                        // Nothing to be done
+                    }
+                    return;
+                }
                 Account account = Account.restoreAccountWithId(mProviderContext,
                         mailbox.mAccountKey);
                 if (account == null) {
@@ -886,6 +895,16 @@
         });
     }
 
+    private static final HashMap<Long, SearchParams> sSearchParamsMap =
+        new HashMap<Long, SearchParams>();
+    
+    public void searchMore(long accountId) throws MessagingException {
+        SearchParams params = sSearchParamsMap.get(accountId);
+        if (params == null) return;
+        params.mOffset += params.mLimit;
+        searchMessages(accountId, params);
+    }
+    
     /**
      * Search for messages on the (IMAP) server; do not call this on the UI thread!
      * @param accountId the id of the account to be searched
@@ -898,7 +917,8 @@
         Mailbox searchMailbox = getSearchMailbox(accountId);
         if (searchMailbox == null) return;
         final long searchMailboxId = searchMailbox.mId;
-
+        // Save this away (per account)
+        sSearchParamsMap.put(accountId, searchParams);
         IEmailService service = getServiceForAccount(accountId);
         if (service != null) {
             // Service implementation
@@ -918,18 +938,20 @@
                 return;
             }
 
-            // Delete existing contents of search mailbox
-            ContentResolver resolver = mContext.getContentResolver();
-            resolver.delete(Message.CONTENT_URI, Message.MAILBOX_KEY + "=" + searchMailboxId,
-                    null);
-            ContentValues cv = new ContentValues();
-            // For now, use the actual query as the name of the mailbox
-            cv.put(Mailbox.DISPLAY_NAME, searchParams.mFilter);
-            // But use the server id of the actual mailbox we're searching; this allows full
-            // message loading to work normally (clever, huh?)
-            cv.put(MailboxColumns.SERVER_ID, actualMailbox.mServerId);
-            resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, searchMailboxId), cv,
-                    null, null);
+            if (searchParams.mOffset == 0) {
+                // Delete existing contents of search mailbox
+                ContentResolver resolver = mContext.getContentResolver();
+                resolver.delete(Message.CONTENT_URI, Message.MAILBOX_KEY + "=" + searchMailboxId,
+                        null);
+                ContentValues cv = new ContentValues();
+                // For now, use the actual query as the name of the mailbox
+                cv.put(Mailbox.DISPLAY_NAME, searchParams.mFilter);
+                // But use the server id of the actual mailbox we're searching; this allows full
+                // message loading to work normally (clever, huh?)
+                cv.put(MailboxColumns.SERVER_ID, actualMailbox.mServerId);
+                resolver.update(ContentUris.withAppendedId(Mailbox.CONTENT_URI, searchMailboxId),
+                        cv, null, null);
+            }
             // Do the search
             if (Email.DEBUG) {
                 Log.d(Logging.LOG_TAG, "Search: " + searchParams.mFilter);
diff --git a/src/com/android/email/MessagingController.java b/src/com/android/email/MessagingController.java
index 19471d1..5517e9c 100644
--- a/src/com/android/email/MessagingController.java
+++ b/src/com/android/email/MessagingController.java
@@ -108,6 +108,14 @@
      */
     private static final String LOCAL_SERVERID_PREFIX = "Local-";
 
+    /**
+     * Cache search results by account; this allows for "load more" support without having to
+     * redo the search (which can be quite slow).  SortableMessage is a smallish class, so memory
+     * shouldn't be an issue
+     */
+    private static final HashMap<Long, SortableMessage[]> sSearchResults =
+        new HashMap<Long, SortableMessage[]>();
+
     private static final ContentValues PRUNE_ATTACHMENT_CV = new ContentValues();
     static {
         PRUNE_ATTACHMENT_CV.putNull(AttachmentColumns.CONTENT_URI);
@@ -572,8 +580,19 @@
         }
     }
 
-    public void searchMailbox(long accountId, SearchParams searchParams, final long destMailboxId)
+    public void searchMailbox(long accountId, SearchParams searchParams, long destMailboxId)
             throws MessagingException {
+        try {
+            searchMailboxImpl(accountId, searchParams, destMailboxId);
+        } finally {
+            // Tell UI that we're done loading any search results (no harm calling this even if we
+            // encountered an error or never sent a "started" message)
+            mListeners.synchronizeMailboxFinished(accountId, destMailboxId, 0, 0, null);
+        }
+    }
+
+    private void searchMailboxImpl(long accountId, SearchParams searchParams,
+            final long destMailboxId) throws MessagingException {
         final Account account = Account.restoreAccountWithId(mContext, accountId);
         final Mailbox mailbox = Mailbox.restoreMailboxWithId(mContext, searchParams.mMailboxId);
         final Mailbox destMailbox = Mailbox.restoreMailboxWithId(mContext, destMailboxId);
@@ -583,77 +602,92 @@
             return;
         }
 
+        // Tell UI that we're loading messages
+        mListeners.synchronizeMailboxStarted(accountId, destMailbox.mId);
+
         Store remoteStore = Store.getInstance(account, mContext, null);
         Folder remoteFolder = remoteStore.getFolder(mailbox.mServerId);
         remoteFolder.open(OpenMode.READ_WRITE, null);
 
-        // Get the "bare" messages (basically uid)
-        Message[] remoteMessages = remoteFolder.getMessages(searchParams, null);
-        int remoteCount = remoteMessages.length;
-        if (remoteCount > 0) {
-            SortableMessage[] sortableMessages = new SortableMessage[remoteCount];
-            int i = 0;
-            for (Message msg : remoteMessages) {
-                sortableMessages[i++] = new SortableMessage(msg, Long.parseLong(msg.getUid()));
-            }
-            // Sort the uid's, most recent first
-            // Note: Not all servers will be nice and return results in the order we request them;
-            // those that do will see messages arrive from newest to oldest (i.e. the "right" order)
-            Arrays.sort(sortableMessages, new Comparator<SortableMessage>() {
-                @Override
-                public int compare(SortableMessage lhs, SortableMessage rhs) {
-                    return lhs.mUid > rhs.mUid ? -1 : lhs.mUid < rhs.mUid ? 1 : 0;
+        SortableMessage[] sortableMessages = new SortableMessage[0];
+        if (searchParams.mOffset == 0) {
+            // Get the "bare" messages (basically uid)
+            Message[] remoteMessages = remoteFolder.getMessages(searchParams, null);
+            int remoteCount = remoteMessages.length;
+            if (remoteCount > 0) {
+                sortableMessages = new SortableMessage[remoteCount];
+                int i = 0;
+                for (Message msg : remoteMessages) {
+                    sortableMessages[i++] = new SortableMessage(msg, Long.parseLong(msg.getUid()));
                 }
-            });
-            final ArrayList<Message> messageList = new ArrayList<Message>();
-            // Now create a list sized for our visible limit and fill it in
-            int messageListSize = Math.min(remoteCount, Email.VISIBLE_LIMIT_DEFAULT);
-            for (i = 0; i < messageListSize; i++) {
-                messageList.add(sortableMessages[i].mMessage);
+                // Sort the uid's, most recent first
+                // Note: Not all servers will be nice and return results in the order of request;
+                // those that do will see messages arrive from newest to oldest
+                Arrays.sort(sortableMessages, new Comparator<SortableMessage>() {
+                    @Override
+                    public int compare(SortableMessage lhs, SortableMessage rhs) {
+                        return lhs.mUid > rhs.mUid ? -1 : lhs.mUid < rhs.mUid ? 1 : 0;
+                    }
+                });
+                sSearchResults.put(accountId, sortableMessages);
             }
-            // Get everything in one pass, rather than two (as in sync); this starts getting us
-            // usable results quickly.
-            FetchProfile fp = new FetchProfile();
-            fp.add(FetchProfile.Item.FLAGS);
-            fp.add(FetchProfile.Item.ENVELOPE);
-            fp.add(FetchProfile.Item.STRUCTURE);
-            fp.add(FetchProfile.Item.BODY_SANE);
-            remoteFolder.fetch(messageList.toArray(new Message[0]), fp,
-                    new MessageRetrievalListener() {
-                        public void messageRetrieved(Message message) {
-                            try {
-                                // Determine if the new message was already known (e.g. partial)
-                                // And create or reload the full message info
-                                EmailContent.Message localMessage = new EmailContent.Message();
-                                try {
-                                    // Copy the fields that are available into the message
-                                    LegacyConversions.updateMessageFields(localMessage,
-                                            message, account.mId, mailbox.mId);
-                                    // Commit the message to the local store
-                                    saveOrUpdate(localMessage, mContext);
-                                    localMessage.mMailboxKey = destMailboxId;
-                                    // We load 50k or so; maybe it's complete, maybe not...
-                                    int flag = EmailContent.Message.FLAG_LOADED_COMPLETE;
-                                    if (message.getSize() > Store.FETCH_BODY_SANE_SUGGESTED_SIZE) {
-                                        flag = EmailContent.Message.FLAG_LOADED_PARTIAL;
-                                    }
-                                    copyOneMessageToProvider(message, localMessage, flag, mContext);
-                                } catch (MessagingException me) {
-                                    Log.e(Logging.LOG_TAG,
-                                            "Error while copying downloaded message." + me);
-                                }
-                            } catch (Exception e) {
-                                Log.e(Logging.LOG_TAG,
-                                        "Error while storing downloaded message." + e.toString());
-                            }
-                        }
+        } else {
+            sortableMessages = sSearchResults.get(accountId);
+        }
 
-                        @Override
-                        public void loadAttachmentProgress(int progress) {
+        int numSearchResults = sortableMessages.length;
+        int numToLoad = Math.min(numSearchResults - searchParams.mOffset, searchParams.mLimit);
+        if (numToLoad <= 0) {
+            return;
+        }
+
+        final ArrayList<Message> messageList = new ArrayList<Message>();
+        for (int i = searchParams.mOffset; i < numToLoad + searchParams.mOffset; i++) {
+            messageList.add(sortableMessages[i].mMessage);
+        }
+        // Get everything in one pass, rather than two (as in sync); this starts getting us
+        // usable results quickly.
+        FetchProfile fp = new FetchProfile();
+        fp.add(FetchProfile.Item.FLAGS);
+        fp.add(FetchProfile.Item.ENVELOPE);
+        fp.add(FetchProfile.Item.STRUCTURE);
+        fp.add(FetchProfile.Item.BODY_SANE);
+        remoteFolder.fetch(messageList.toArray(new Message[0]), fp,
+                new MessageRetrievalListener() {
+            public void messageRetrieved(Message message) {
+                try {
+                    // Determine if the new message was already known (e.g. partial)
+                    // And create or reload the full message info
+                    EmailContent.Message localMessage = new EmailContent.Message();
+                    try {
+                        // Copy the fields that are available into the message
+                        LegacyConversions.updateMessageFields(localMessage,
+                                message, account.mId, mailbox.mId);
+                        // Commit the message to the local store
+                        saveOrUpdate(localMessage, mContext);
+                        localMessage.mMailboxKey = destMailboxId;
+                        // We load 50k or so; maybe it's complete, maybe not...
+                        int flag = EmailContent.Message.FLAG_LOADED_COMPLETE;
+                        if (message.getSize() > Store.FETCH_BODY_SANE_SUGGESTED_SIZE) {
+                            flag = EmailContent.Message.FLAG_LOADED_PARTIAL;
                         }
-                    });
-        }
-        }
+                        copyOneMessageToProvider(message, localMessage, flag, mContext);
+                    } catch (MessagingException me) {
+                        Log.e(Logging.LOG_TAG,
+                                "Error while copying downloaded message." + me);
+                    }
+                } catch (Exception e) {
+                    Log.e(Logging.LOG_TAG,
+                            "Error while storing downloaded message." + e.toString());
+                }
+            }
+
+            @Override
+            public void loadAttachmentProgress(int progress) {
+            }
+        });
+    }
+
 
     /**
      * Generic synchronizer - used for POP3 and IMAP.