From 611bae3fb4e8e572303b16c98a897493753a6381 Mon Sep 17 00:00:00 2001 From: Sander Bogaert Date: Fri, 12 Oct 2012 12:30:26 +0200 Subject: [PATCH 01/12] Created search package to hold the framework and moved over the SearchSpecification class ( refactor ). --- src/com/fsck/k9/K9.java | 4 ++-- src/com/fsck/k9/SearchAccount.java | 1 + src/com/fsck/k9/activity/Accounts.java | 2 +- src/com/fsck/k9/activity/FolderList.java | 2 +- src/com/fsck/k9/activity/LauncherShortcuts.java | 2 +- src/com/fsck/k9/activity/MessageList.java | 2 +- src/com/fsck/k9/controller/MessagingController.java | 2 +- src/com/fsck/k9/preferences/GlobalSettings.java | 4 ++-- src/com/fsck/k9/{ => search}/SearchSpecification.java | 2 +- 9 files changed, 11 insertions(+), 10 deletions(-) rename src/com/fsck/k9/{ => search}/SearchSpecification.java (91%) diff --git a/src/com/fsck/k9/K9.java b/src/com/fsck/k9/K9.java index e6caa157e..ef0f21fb0 100644 --- a/src/com/fsck/k9/K9.java +++ b/src/com/fsck/k9/K9.java @@ -579,8 +579,8 @@ public class K9 extends Application { public static void loadPrefs(Preferences prefs) { SharedPreferences sprefs = prefs.getPreferences(); - DEBUG = sprefs.getBoolean("enableDebugLogging", false); - DEBUG_SENSITIVE = sprefs.getBoolean("enableSensitiveLogging", false); + DEBUG = sprefs.getBoolean("enableDebugLogging", true); + DEBUG_SENSITIVE = sprefs.getBoolean("enableSensitiveLogging", true); mAnimations = sprefs.getBoolean("animations", true); mGesturesEnabled = sprefs.getBoolean("gesturesEnabled", false); mUseVolumeKeysForNavigation = sprefs.getBoolean("useVolumeKeysForNavigation", false); diff --git a/src/com/fsck/k9/SearchAccount.java b/src/com/fsck/k9/SearchAccount.java index bf2f25570..97e2092ee 100644 --- a/src/com/fsck/k9/SearchAccount.java +++ b/src/com/fsck/k9/SearchAccount.java @@ -6,6 +6,7 @@ import java.util.UUID; import android.content.Context; import com.fsck.k9.mail.Flag; +import com.fsck.k9.search.SearchSpecification; /** * This is a meta-Account that represents one or more accounts with filters on them. The filter specification diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java index f45fe52e5..cbaaaa8ef 100644 --- a/src/com/fsck/k9/activity/Accounts.java +++ b/src/com/fsck/k9/activity/Accounts.java @@ -63,7 +63,6 @@ import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.R; import com.fsck.k9.SearchAccount; -import com.fsck.k9.SearchSpecification; import com.fsck.k9.activity.misc.ExtendedAsyncTask; import com.fsck.k9.activity.misc.NonConfigurationInstance; import com.fsck.k9.activity.setup.AccountSettings; @@ -80,6 +79,7 @@ import com.fsck.k9.mail.Transport; import com.fsck.k9.mail.internet.MimeUtility; import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.WebDavStore; +import com.fsck.k9.search.SearchSpecification; import com.fsck.k9.view.ColorChip; import com.fsck.k9.preferences.SettingsExporter; import com.fsck.k9.preferences.SettingsImportExportException; diff --git a/src/com/fsck/k9/activity/FolderList.java b/src/com/fsck/k9/activity/FolderList.java index fa2deb5c9..dc034c4ba 100644 --- a/src/com/fsck/k9/activity/FolderList.java +++ b/src/com/fsck/k9/activity/FolderList.java @@ -52,7 +52,6 @@ import com.fsck.k9.FontSizes; import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.R; -import com.fsck.k9.SearchSpecification; import com.fsck.k9.activity.FolderList.FolderListAdapter.FolderListFilter; import com.fsck.k9.activity.misc.ActionBarNavigationSpinner; import com.fsck.k9.activity.setup.AccountSettings; @@ -68,6 +67,7 @@ import com.fsck.k9.mail.Folder; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.store.LocalStore.LocalFolder; +import com.fsck.k9.search.SearchSpecification; import com.fsck.k9.service.MailService; /** diff --git a/src/com/fsck/k9/activity/LauncherShortcuts.java b/src/com/fsck/k9/activity/LauncherShortcuts.java index 415689057..add1d837a 100644 --- a/src/com/fsck/k9/activity/LauncherShortcuts.java +++ b/src/com/fsck/k9/activity/LauncherShortcuts.java @@ -7,7 +7,7 @@ import android.os.Parcelable; import com.fsck.k9.Account; import com.fsck.k9.BaseAccount; import com.fsck.k9.R; -import com.fsck.k9.SearchSpecification; +import com.fsck.k9.search.SearchSpecification; public class LauncherShortcuts extends AccountList { @Override diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java index eb7c91ee4..d984efdbe 100644 --- a/src/com/fsck/k9/activity/MessageList.java +++ b/src/com/fsck/k9/activity/MessageList.java @@ -24,7 +24,6 @@ import com.fsck.k9.Account.SortType; import com.fsck.k9.K9; import com.fsck.k9.Preferences; import com.fsck.k9.R; -import com.fsck.k9.SearchSpecification; import com.fsck.k9.activity.misc.SwipeGestureDetector.OnSwipeGestureListener; import com.fsck.k9.activity.setup.AccountSettings; import com.fsck.k9.activity.setup.FolderSettings; @@ -35,6 +34,7 @@ import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.store.StorageManager; +import com.fsck.k9.search.SearchSpecification; /** diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index e4cf48734..bc67559ae 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -40,7 +40,6 @@ import com.fsck.k9.K9.Intents; import com.fsck.k9.NotificationSetting; import com.fsck.k9.Preferences; import com.fsck.k9.R; -import com.fsck.k9.SearchSpecification; import com.fsck.k9.activity.FolderList; import com.fsck.k9.activity.MessageList; import com.fsck.k9.helper.NotificationBuilder; @@ -70,6 +69,7 @@ import com.fsck.k9.mail.store.LocalStore.LocalMessage; import com.fsck.k9.mail.store.LocalStore.PendingCommand; import com.fsck.k9.mail.store.UnavailableAccountException; import com.fsck.k9.mail.store.UnavailableStorageException; +import com.fsck.k9.search.SearchSpecification; /** diff --git a/src/com/fsck/k9/preferences/GlobalSettings.java b/src/com/fsck/k9/preferences/GlobalSettings.java index e49face93..019391003 100644 --- a/src/com/fsck/k9/preferences/GlobalSettings.java +++ b/src/com/fsck/k9/preferences/GlobalSettings.java @@ -64,10 +64,10 @@ public class GlobalSettings { new V(1, new DateFormatSetting(DateFormatter.DEFAULT_FORMAT)) )); s.put("enableDebugLogging", Settings.versions( - new V(1, new BooleanSetting(false)) + new V(1, new BooleanSetting(true)) )); s.put("enableSensitiveLogging", Settings.versions( - new V(1, new BooleanSetting(false)) + new V(1, new BooleanSetting(true)) )); s.put("fontSizeAccountDescription", Settings.versions( new V(1, new FontSizeSetting(FontSizes.SMALL)) diff --git a/src/com/fsck/k9/SearchSpecification.java b/src/com/fsck/k9/search/SearchSpecification.java similarity index 91% rename from src/com/fsck/k9/SearchSpecification.java rename to src/com/fsck/k9/search/SearchSpecification.java index a1f1de0a8..e08032ff5 100644 --- a/src/com/fsck/k9/SearchSpecification.java +++ b/src/com/fsck/k9/search/SearchSpecification.java @@ -1,5 +1,5 @@ -package com.fsck.k9; +package com.fsck.k9.search; import com.fsck.k9.mail.Flag; From 5c6552cbf3462c3f57b7f092dd4198bda4b5ca52 Mon Sep 17 00:00:00 2001 From: Sander Bogaert Date: Fri, 12 Oct 2012 13:22:54 +0200 Subject: [PATCH 02/12] Adding the 3 core classes for the search framework. ConditionsTreeNode, LocalSearch and SearchSpecification. --- .../fsck/k9/search/ConditionsTreeNode.java | 365 +++++++++++++++++ src/com/fsck/k9/search/LocalSearch.java | 374 ++++++++++++++++++ .../fsck/k9/search/SearchSpecification.java | 191 ++++++++- 3 files changed, 917 insertions(+), 13 deletions(-) create mode 100644 src/com/fsck/k9/search/ConditionsTreeNode.java create mode 100644 src/com/fsck/k9/search/LocalSearch.java diff --git a/src/com/fsck/k9/search/ConditionsTreeNode.java b/src/com/fsck/k9/search/ConditionsTreeNode.java new file mode 100644 index 000000000..fbb31a7b9 --- /dev/null +++ b/src/com/fsck/k9/search/ConditionsTreeNode.java @@ -0,0 +1,365 @@ +package com.fsck.k9.search; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Stack; + +import android.database.Cursor; +import android.os.Parcel; +import android.os.Parcelable; + +import com.fsck.k9.search.SearchSpecification.ATTRIBUTE; +import com.fsck.k9.search.SearchSpecification.SEARCHFIELD; +import com.fsck.k9.search.SearchSpecification.SearchCondition; + +/** + * This class stores search conditions. It's basically a boolean expression binary tree. + * The output will be SQL queries ( obtained by traversing inorder ). + * + * TODO removing conditions from the tree + * TODO implement NOT as a node again + * + * @author dzan + */ +public class ConditionsTreeNode implements Parcelable{ + + public enum OPERATOR { + AND, OR, CONDITION; + } + + public ConditionsTreeNode mLeft; + public ConditionsTreeNode mRight; + public ConditionsTreeNode mParent; + + /* + * If mValue isn't CONDITION then mCondition contains a real + * condition, otherwise it's null. + */ + public OPERATOR mValue; + public SearchCondition mCondition; + + /* + * Used for storing and retrieving the tree to/from the database. + * The algorithm is called "modified preorder tree traversal". + */ + public int mLeftMPTTMarker; + public int mRightMPTTMarker; + + + /////////////////////////////////////////////////////////////// + // Static Helpers to restore a tree from a database cursor + /////////////////////////////////////////////////////////////// + /** + * Builds a condition tree starting from a database cursor. The cursor + * should point to rows representing the nodes of the tree. + * + * @param cursor Cursor pointing to the first of a bunch or rows. Each rows + * should contains 1 tree node. + * @return A condition tree. + */ + public static ConditionsTreeNode buildTreeFromDB(Cursor cursor) { + Stack stack = new Stack(); + ConditionsTreeNode tmp = null; + + // root node + if (cursor.moveToFirst()) { + tmp = buildNodeFromRow(cursor); + stack.push(tmp); + } + + // other nodes + while (cursor.moveToNext()) { + tmp = buildNodeFromRow(cursor); + if (tmp.mRightMPTTMarker < stack.peek().mRightMPTTMarker ){ + stack.peek().mLeft = tmp; + stack.push(tmp); + } else { + while (stack.peek().mRightMPTTMarker < tmp.mRightMPTTMarker) { + stack.pop(); + } + stack.peek().mRight = tmp; + } + } + return tmp; + } + + /** + * Converts a single database row to a single condition node. + * + * @param cursor Cursor pointing to the row we want to convert. + * @return A single ConditionsTreeNode + */ + private static ConditionsTreeNode buildNodeFromRow(Cursor cursor) { + ConditionsTreeNode result = null; + SearchCondition condition = null; + + OPERATOR tmpValue = ConditionsTreeNode.OPERATOR.valueOf(cursor.getString(5)); + + if (tmpValue == OPERATOR.CONDITION) { + condition = new SearchCondition(SEARCHFIELD.valueOf(cursor.getString(0)), + ATTRIBUTE.valueOf(cursor.getString(2)), cursor.getString(1)); + } + + result = new ConditionsTreeNode(condition); + result.mValue = tmpValue; + result.mLeftMPTTMarker = cursor.getInt(3); + result.mRightMPTTMarker = cursor.getInt(4); + + return result; + } + + + /////////////////////////////////////////////////////////////// + // Constructors + /////////////////////////////////////////////////////////////// + public ConditionsTreeNode(SearchCondition condition) { + mParent = null; + mCondition = condition; + mValue = OPERATOR.CONDITION; + } + + public ConditionsTreeNode(ConditionsTreeNode parent, OPERATOR op) { + mParent = parent; + mValue = op; + mCondition = null; + } + + + /////////////////////////////////////////////////////////////// + // Public modifiers + /////////////////////////////////////////////////////////////// + /** + * Adds the expression as the second argument of an AND + * clause to this node. + * + * @param expr Expression to 'AND' with. + * @return New top AND node. + * @throws Exception + */ + public ConditionsTreeNode and(ConditionsTreeNode expr) throws Exception { + return add(expr, OPERATOR.AND); + } + + /** + * Adds the expression as the second argument of an OR + * clause to this node. + * + * @param expr Expression to 'OR' with. + * @return New top OR node. + * @throws Exception + */ + public ConditionsTreeNode or(ConditionsTreeNode expr) throws Exception { + return add(expr, OPERATOR.OR); + } + + /** + * This applies the MPTT labeling to the subtree of which this node + * is the root node. + * + * For a description on MPTT see: + * http://www.sitepoint.com/hierarchical-data-database-2/ + */ + public void applyMPTTLabel() { + applyMPTTLabel(1); + } + + + /////////////////////////////////////////////////////////////// + // Public accessors + /////////////////////////////////////////////////////////////// + /** + * Returns the condition stored in this node. + * @return Condition stored in the node. + */ + public SearchCondition getCondition() { + return mCondition; + } + + + /** + * This will traverse the tree inorder and call toString recursively resulting + * in a valid SQL where clause. + */ + @Override + public String toString() { + return (mLeft == null ? "" : "(" + mLeft + ")") + + " " + ( mCondition == null ? mValue.name() : mCondition ) + " " + + (mRight == null ? "" : "(" + mRight + ")") ; + } + + /** + * Get a set of all the leaves in the tree. + * @return Set of all the leaves. + */ + public HashSet getLeafSet() { + HashSet leafSet = new HashSet(); + return getLeafSet(leafSet); + } + + /** + * Returns a list of all the nodes in the subtree of which this node + * is the root. The list contains the nodes in a pre traversal order. + * + * @return List of all nodes in subtree in preorder. + */ + public List preorder() { + ArrayList result = new ArrayList(); + Stack stack = new Stack(); + stack.push(this); + + while(!stack.isEmpty()) { + ConditionsTreeNode current = stack.pop( ); + if( current.mLeft != null ) stack.push( current.mLeft ); + if( current.mRight != null ) stack.push( current.mRight ); + result.add(current); + } + + return result; + } + + + /////////////////////////////////////////////////////////////// + // Private class logic + /////////////////////////////////////////////////////////////// + /** + * Adds two new ConditionTreeNodes, one for the operator and one for the + * new condition. The current node will end up on the same level as the + * one provided in the arguments, they will be siblings. Their common + * parent node will be one containing the operator provided in the arguments. + * The method will update all the required references so the tree ends up in + * a valid state. + * + * This method only supports node arguments with a null parent node. + * + * @param Node to add. + * @param Operator that will connect the new node with this one. + * @return New parent node, containing the operator. + * @throws Exception Throws when the provided new node does not have a null parent. + */ + private ConditionsTreeNode add(ConditionsTreeNode node, OPERATOR op) throws Exception{ + if (node.mParent != null) { + throw new Exception("Can only add new expressions from root node down."); + } + + ConditionsTreeNode tmpNode = new ConditionsTreeNode(mParent, op); + tmpNode.mLeft = this; + tmpNode.mRight = node; + + if (mParent != null) { + mParent.updateChild(this, tmpNode); + } + this.mParent = tmpNode; + + node.mParent = tmpNode; + + return tmpNode; + } + + /** + * Helper method that replaces a child of the current node with a new node. + * If the provided old child node was the left one, left will be replaced with + * the new one. Same goes for the right one. + * + * @param oldChild Old child node to be replaced. + * @param newChild New child node. + */ + private void updateChild(ConditionsTreeNode oldChild, ConditionsTreeNode newChild) { + // we can compare objects id's because this is the desired behaviour in this case + if (mLeft == oldChild) { + mLeft = newChild; + } else if (mRight == oldChild) { + mRight = newChild; + } + } + + /** + * Recursive function to gather all the leaves in the subtree of which + * this node is the root. + * + * @param leafSet Leafset that's being built. + * @return Set of leaves being completed. + */ + private HashSet getLeafSet(HashSet leafSet) { + // if we ended up in a leaf, add ourself and return + if (mLeft == null && mRight == null) { + leafSet.add(this); + return leafSet; + // we didn't end up in a leaf + } else { + if (mLeft != null) { + mLeft.getLeafSet(leafSet); + } + + if (mRight != null) { + mRight.getLeafSet(leafSet); + } + return leafSet; + } + } + + /** + * This applies the MPTT labeling to the subtree of which this node + * is the root node. + * + * For a description on MPTT see: + * http://www.sitepoint.com/hierarchical-data-database-2/ + */ + private int applyMPTTLabel(int label) { + mLeftMPTTMarker = label; + if (mLeft != null){ + label = mLeft.applyMPTTLabel(label += 1); + } + if (mRight != null){ + label = mRight.applyMPTTLabel(label += 1); + } + ++label; + mRightMPTTMarker = label; + return label; + } + + + /////////////////////////////////////////////////////////////// + // Parcelable + // + // This whole class has to be parcelable because it's passed + // on through intents. + /////////////////////////////////////////////////////////////// + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeInt(mValue.ordinal()); + dest.writeParcelable(mCondition, flags); + dest.writeParcelable(mLeft, flags); + dest.writeParcelable(mRight, flags); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public ConditionsTreeNode createFromParcel(Parcel in) { + return new ConditionsTreeNode(in); + } + + public ConditionsTreeNode[] newArray(int size) { + return new ConditionsTreeNode[size]; + } + }; + + private ConditionsTreeNode(Parcel in) { + mValue = OPERATOR.values()[in.readInt()]; + mCondition = in.readParcelable(ConditionsTreeNode.class.getClassLoader()); + mLeft = in.readParcelable(ConditionsTreeNode.class.getClassLoader()); + mRight = in.readParcelable(ConditionsTreeNode.class.getClassLoader()); + mParent = null; + if (mLeft != null) { + mLeft.mParent = this; + } + if (mRight != null) { + mRight.mParent = this; + } + } +} diff --git a/src/com/fsck/k9/search/LocalSearch.java b/src/com/fsck/k9/search/LocalSearch.java new file mode 100644 index 000000000..48ca3ca7c --- /dev/null +++ b/src/com/fsck/k9/search/LocalSearch.java @@ -0,0 +1,374 @@ +package com.fsck.k9.search; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import android.os.Parcel; +import android.os.Parcelable; + +import com.fsck.k9.mail.Flag; + +/** + * This class represents a local search. + + * Removing conditions could be done through matching there unique id in the leafset and then + * removing them from the tree. + * + * @author dzan + * + * TODO implement a complete addAllowedFolder method + * TODO conflicting conditions check on add + * TODO duplicate condition checking? + * TODO assign each node a unique id that's used to retrieve it from the leaveset and remove. + * + */ + +public class LocalSearch implements SearchSpecification { + + private String mName; + private boolean mPredefined; + + // since the uuid isn't in the message table it's not in the tree neither + private HashSet mAccountUuids = new HashSet(); + private ConditionsTreeNode mConditions = null; + private HashSet mLeafSet = new HashSet(); + + + /////////////////////////////////////////////////////////////// + // Constructors + /////////////////////////////////////////////////////////////// + /** + * Use this only if the search won't be saved. Saved searches need + * a name! + */ + public LocalSearch(){} + + /** + * + * @param name + */ + public LocalSearch(String name) { + this.mName = name; + } + + /** + * Use this constructor when you know what you'r doing. Normally it's only used + * when restoring these search objects from the database. + * + * @param name Name of the search + * @param searchConditions SearchConditions, may contains flags and folders + * @param accounts Relative Account's uuid's + * @param predefined Is this a predefined search or a user created one? + */ + protected LocalSearch(String name, ConditionsTreeNode searchConditions, + String accounts, boolean predefined) { + this(name); + mConditions = searchConditions; + mPredefined = predefined; + mLeafSet = new HashSet(); + if (mConditions != null) { + mLeafSet.addAll(mConditions.getLeafSet()); + } + + // initialize accounts + if (accounts != null) { + for (String account : accounts.split(",")) { + mAccountUuids.add(account); + } + } else { + // impossible but still not unrecoverable + } + } + + + /////////////////////////////////////////////////////////////// + // Public manipulation methods + /////////////////////////////////////////////////////////////// + /** + * Sets the name of the saved search. If one existed it will + * be overwritten. + * + * @param name Name to be set. + */ + public void setName(String name) { + this.mName = name; + } + + /** + * Add a new account to the search. When no accounts are + * added manually we search all accounts on the device. + * + * @param uuid Uuid of the account to be added. + */ + public void addAccountUuid(String uuid) { + if (uuid.equals(ALL_ACCOUNTS)) { + mAccountUuids.clear(); + } + mAccountUuids.add(uuid); + } + + /** + * Adds all the account uuids in the provided array to + * be matched by the seach. + * + * @param accountUuids + */ + public void addAccountUuids(String[] accountUuids) { + for (String acc : accountUuids) { + addAccountUuid(acc); + } + } + + /** + * Removes an account UUID from the current search. + * + * @param uuid Account UUID to remove. + * @return True if removed, false otherwise. + */ + public boolean removeAccountUuid(String uuid) { + return mAccountUuids.remove(uuid); + } + + /** + * Adds the provided node as the second argument of an AND + * clause to this node. + * + * @param field Message table field to match against. + * @param string Value to look for. + * @param contains Attribute to use when matching. + * + * @throws IllegalConditionException + */ + public void and(SEARCHFIELD field, String value, ATTRIBUTE attribute) { + and(new SearchCondition(field, attribute, value)); + } + + /** + * Adds the provided condition as the second argument of an AND + * clause to this node. + * + * @param condition Condition to 'AND' with. + * @return New top AND node, new root. + */ + public ConditionsTreeNode and(SearchCondition condition) { + ConditionsTreeNode tmp = new ConditionsTreeNode(condition); + return and(tmp); + } + + /** + * Adds the provided node as the second argument of an AND + * clause to this node. + * + * @param node Node to 'AND' with. + * @return New top AND node, new root. + */ + public ConditionsTreeNode and(ConditionsTreeNode node) { + try { + mLeafSet.add(node); + + if (mConditions == null) { + mConditions = node; + return node; + } + + mConditions = mConditions.and(node); + return mConditions; + } catch (Exception e) { + // IMPOSSIBLE! + return null; + } + } + + /** + * Adds the provided condition as the second argument of an OR + * clause to this node. + * + * @param condition Condition to 'OR' with. + * @return New top OR node, new root. + */ + public ConditionsTreeNode or(SearchCondition condition) { + ConditionsTreeNode tmp = new ConditionsTreeNode(condition); + return or(tmp); + } + + /** + * Adds the provided node as the second argument of an OR + * clause to this node. + * + * @param node Node to 'OR' with. + * @return New top OR node, new root. + */ + public ConditionsTreeNode or(ConditionsTreeNode node) { + try { + mLeafSet.add(node); + + if (mConditions == null) { + mConditions = node; + return node; + } + + mConditions = mConditions.or(node); + return mConditions; + } catch (Exception e) { + // IMPOSSIBLE! + return null; + } + } + + /** + * Add all the flags to this node as required flags. The + * provided flags will be combined using AND with the root. + * + * @param requiredFlags Array of required flags. + */ + public void allRequiredFlags(Flag[] requiredFlags) { + if (requiredFlags != null) { + for (Flag f : requiredFlags) { + and(new SearchCondition(SEARCHFIELD.FLAG, ATTRIBUTE.CONTAINS, f.name())); + } + } + } + + /** + * Add all the flags to this node as forbidden flags. The + * provided flags will be combined using AND with the root. + * + * @param forbiddenFlags Array of forbidden flags. + */ + public void allForbiddenFlags(Flag[] forbiddenFlags) { + if (forbiddenFlags != null) { + for (Flag f : forbiddenFlags) { + and(new SearchCondition(SEARCHFIELD.FLAG, ATTRIBUTE.NOT_CONTAINS, f.name())); + } + } + } + + /** + * TODO + * FOR NOW: And the folder with the root. + * + * Add the folder as another folder to search in. The folder + * will be added AND to the root if no 'folder subtree' was found. + * Otherwise the folder will be added OR to that tree. + * + * @param name Name of the folder to add. + */ + public void addAllowedFolder(String name) { + /* + * TODO find folder sub-tree + * - do and on root of it & rest of search + * - do or between folder nodes + */ + and(new SearchCondition(SEARCHFIELD.FOLDER, ATTRIBUTE.EQUALS, name)); + } + + /* + * TODO make this more advanced! + * This is a temporarely solution that does NOT WORK for + * real searches because of possible extra conditions to a folder requirement. + */ + public List getFolderNames() { + ArrayList results = new ArrayList(); + for (ConditionsTreeNode node : mLeafSet) { + if (node.mCondition.field == SEARCHFIELD.FOLDER + && node.mCondition.attribute == ATTRIBUTE.EQUALS) { + results.add(node.mCondition.value); + } + } + return results; + } + + /** + * Gets the leafset of the related condition tree. + * + * @return All the leaf conditions as a set. + */ + public Set getLeafSet() { + return mLeafSet; + } + + /////////////////////////////////////////////////////////////// + // Public accesor methods + /////////////////////////////////////////////////////////////// + /** + * Returns the name of the saved search. + * + * @return Name of the search. + */ + public String getName() { + return mName; + } + + /** + * Checks if this search was hard coded and shipped with K-9 + * + * @return True is search was shipped with K-9 + */ + public boolean isPredefined() { + return mPredefined; + } + + /** + * Returns all the account uuids that this search will try to + * match against. + * + * @return Array of account uuids. + */ + @Override + public String[] getAccountUuids() { + if (mAccountUuids.size() == 0) { + return new String[] {SearchSpecification.ALL_ACCOUNTS}; + } + + String[] tmp = new String[mAccountUuids.size()]; + mAccountUuids.toArray(tmp); + return tmp; + } + + /** + * Get the condition tree. + * + * @return The root node of the related conditions tree. + */ + @Override + public ConditionsTreeNode getConditions() { + return mConditions; + } + + /////////////////////////////////////////////////////////////// + // Parcelable + /////////////////////////////////////////////////////////////// + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(mName); + dest.writeByte((byte) (mPredefined ? 1 : 0)); + dest.writeStringList(new ArrayList(mAccountUuids)); + dest.writeParcelable(mConditions, flags); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public LocalSearch createFromParcel(Parcel in) { + return new LocalSearch(in); + } + + public LocalSearch[] newArray(int size) { + return new LocalSearch[size]; + } + }; + + public LocalSearch(Parcel in) { + mName = in.readString(); + mPredefined = in.readByte() == 1; + mAccountUuids.addAll(in.createStringArrayList()); + mConditions = in.readParcelable(LocalSearch.class.getClassLoader()); + mLeafSet = mConditions.getLeafSet(); + } +} \ No newline at end of file diff --git a/src/com/fsck/k9/search/SearchSpecification.java b/src/com/fsck/k9/search/SearchSpecification.java index e08032ff5..3c422c64e 100644 --- a/src/com/fsck/k9/search/SearchSpecification.java +++ b/src/com/fsck/k9/search/SearchSpecification.java @@ -1,19 +1,184 @@ - package com.fsck.k9.search; -import com.fsck.k9.mail.Flag; - -public interface SearchSpecification { - - public Flag[] getRequiredFlags(); - - public Flag[] getForbiddenFlags(); - - public boolean isIntegrate(); - - public String getQuery(); +import android.os.Parcel; +import android.os.Parcelable; +public interface SearchSpecification extends Parcelable { + + /** + * Get all the uuids of accounts this search acts on. + * @return Array of uuids. + */ public String[] getAccountUuids(); + + /** + * Returns the search's name if it was named. + * @return Name of the search. + */ + public String getName(); + + /** + * Returns the root node of the condition tree accompanying + * the search. + * + * @return Root node of conditions tree. + */ + public ConditionsTreeNode getConditions(); + + /* + * Some meta names for certain conditions. + */ + public static final String ALL_ACCOUNTS = "allAccounts"; + public static final String GENERIC_INBOX_NAME = "genericInboxName"; + + /////////////////////////////////////////////////////////////// + // ATTRIBUTE enum + /////////////////////////////////////////////////////////////// + public enum ATTRIBUTE { + CONTAINS(false), EQUALS(false), STARTSWITH(false), ENDSWITH(false), + NOT_CONTAINS(true), NOT_EQUALS(true), NOT_STARTSWITH(true), NOT_ENDSWITH(true); + + private boolean mNegation; + + private ATTRIBUTE(boolean negation) { + this.mNegation = negation; + } + + public String formQuery(String value) { + String queryPart = ""; + + switch (this) { + case NOT_CONTAINS: + case CONTAINS: + queryPart = "'%"+value+"%'"; + break; + case NOT_EQUALS: + case EQUALS: + queryPart = "'"+value+"'"; + break; + case NOT_STARTSWITH: + case STARTSWITH: + queryPart = "'%"+value+"'"; + break; + case NOT_ENDSWITH: + case ENDSWITH: + queryPart = "'"+value+"%'"; + break; + default: queryPart = "'"+value+"'"; + } + + return (mNegation ? " NOT LIKE " : " LIKE ") + queryPart; + } + }; + + /////////////////////////////////////////////////////////////// + // SEARCHFIELD enum + /////////////////////////////////////////////////////////////// + /* + * Using an enum in order to have more robust code. Users ( & coders ) + * are prevented from passing illegal fields. No database overhead + * when invalid fields passed. + * + * By result, only the fields in here are searchable. + * + * Fields not in here at this moment ( and by effect not searchable ): + * id, html_content, internal_date, message_id, + * preview, mime_type + * + */ + public enum SEARCHFIELD { + SUBJECT("subject"), DATE("date"), UID("uid"), FLAG("flags"), + SENDER("sender_list"), TO("to_list"), CC("cc_list"), FOLDER("folder_id"), + BCC("bcc_list"), REPLY_TO("reply_to_list"), MESSAGE("text_content"), + ATTACHMENT_COUNT("attachment_count"), DELETED("deleted"); - public String[] getFolderNames(); + private String dbName; + + private SEARCHFIELD(String dbName) { + this.dbName = dbName; + } + + public String getDatabaseName() { + return dbName; + } + } + + + /////////////////////////////////////////////////////////////// + // SearchCondition class + /////////////////////////////////////////////////////////////// + /** + * This class represents 1 value for a certain search field. One + * value consists of three things: + * an attribute: equals, starts with, contains,... + * a searchfield: date, flags, sender, subject,... + * a value: "apple", "jesse",.. + * + * @author dzan + */ + public class SearchCondition implements Parcelable{ + public String value; + public ATTRIBUTE attribute; + public SEARCHFIELD field; + + public SearchCondition(SEARCHFIELD field, ATTRIBUTE attribute, String value) { + this.value = value; + this.attribute = attribute; + this.field = field; + } + + private SearchCondition(Parcel in) { + this.value = in.readString(); + this.attribute = ATTRIBUTE.values()[in.readInt()]; + this.field = SEARCHFIELD.values()[in.readInt()]; + } + + public String toHumanString() { + return field.toString() + attribute.toString(); + } + + @Override + public String toString() { + return field.getDatabaseName() + attribute.formQuery(value); + } + + @Override + public boolean equals(Object o) { + if (o instanceof SearchCondition) { + SearchCondition tmp = (SearchCondition) o; + if (tmp.attribute == attribute + && tmp.value.equals(value) + && tmp.field == field) { + return true; + } else { + return false; + } + } else { + return false; + } + } + + @Override + public int describeContents() { + return 0; + } + + @Override + public void writeToParcel(Parcel dest, int flags) { + dest.writeString(value); + dest.writeInt(attribute.ordinal()); + dest.writeInt(field.ordinal()); + } + + public static final Parcelable.Creator CREATOR + = new Parcelable.Creator() { + public SearchCondition createFromParcel(Parcel in) { + return new SearchCondition(in); + } + + public SearchCondition[] newArray(int size) { + return new SearchCondition[size]; + } + }; + } } \ No newline at end of file From d27f909600219f5442f1650eb2b74700ab1af292 Mon Sep 17 00:00:00 2001 From: Sander Bogaert Date: Sat, 13 Oct 2012 08:53:00 -0400 Subject: [PATCH 03/12] Add new search logic to the MessagingController and LocalStore classes. --- .../k9/controller/MessagingController.java | 160 ++++-------------- src/com/fsck/k9/mail/store/LocalStore.java | 155 ++++++++--------- 2 files changed, 98 insertions(+), 217 deletions(-) diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index bc67559ae..dfb638828 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -69,6 +69,7 @@ import com.fsck.k9.mail.store.LocalStore.LocalMessage; import com.fsck.k9.mail.store.LocalStore.PendingCommand; import com.fsck.k9.mail.store.UnavailableAccountException; import com.fsck.k9.mail.store.UnavailableStorageException; +import com.fsck.k9.search.LocalSearch; import com.fsck.k9.search.SearchSpecification; @@ -619,136 +620,39 @@ public class MessagingController implements Runnable { } } - public void searchLocalMessages(SearchSpecification searchSpecification, final Message[] messages, final MessagingListener listener) { - searchLocalMessages(searchSpecification.getAccountUuids(), searchSpecification.getFolderNames(), messages, - searchSpecification.getQuery(), searchSpecification.isIntegrate(), searchSpecification.getRequiredFlags(), searchSpecification.getForbiddenFlags(), listener); - } - - /** * Find all messages in any local account which match the query 'query' * @throws MessagingException */ - public void searchLocalMessages(final String[] accountUuids, final String[] folderNames, final Message[] messages, final String query, final boolean integrate, - final Flag[] requiredFlags, final Flag[] forbiddenFlags, final MessagingListener listener) { - if (K9.DEBUG) { - Log.i(K9.LOG_TAG, "searchLocalMessages (" - + "accountUuids=" + Utility.combine(accountUuids, ',') - + ", folderNames = " + Utility.combine(folderNames, ',') - + ", messages.size() = " + (messages != null ? messages.length : -1) - + ", query = " + query - + ", integrate = " + integrate - + ", requiredFlags = " + Utility.combine(requiredFlags, ',') - + ", forbiddenFlags = " + Utility.combine(forbiddenFlags, ',') - + ")"); - } - + public void searchLocalMessages(final LocalSearch search, final MessagingListener listener) { threadPool.execute(new Runnable() { @Override public void run() { - searchLocalMessagesSynchronous(accountUuids, folderNames, messages, query, integrate, requiredFlags, forbiddenFlags, listener); + searchLocalMessagesSynchronous(search, listener); } }); } - public void searchLocalMessagesSynchronous(final String[] accountUuids, final String[] folderNames, final Message[] messages, final String query, final boolean integrate, final Flag[] requiredFlags, final Flag[] forbiddenFlags, final MessagingListener listener) { - + + public void searchLocalMessagesSynchronous(final LocalSearch search, final MessagingListener listener) { final AccountStats stats = new AccountStats(); - final Set accountUuidsSet = new HashSet(); - if (accountUuids != null) { - accountUuidsSet.addAll(Arrays.asList(accountUuids)); - } - final Preferences prefs = Preferences.getPreferences(mApplication.getApplicationContext()); - List foldersToSearch = null; - boolean displayableOnly = false; - boolean noSpecialFolders = true; - for (final Account account : prefs.getAvailableAccounts()) { - if (accountUuids != null && !accountUuidsSet.contains(account.getUuid())) { - continue; - } - - if (accountUuids != null && accountUuidsSet.contains(account.getUuid())) { - displayableOnly = true; - noSpecialFolders = true; - } else if (!integrate && folderNames == null) { - Account.Searchable searchableFolders = account.getSearchableFolders(); - switch (searchableFolders) { - case NONE: - continue; - case DISPLAYABLE: - displayableOnly = true; - break; - - } - } - List messagesToSearch = null; - if (messages != null) { - messagesToSearch = new LinkedList(); - for (Message message : messages) { - if (message.getFolder().getAccount().getUuid().equals(account.getUuid())) { - messagesToSearch.add(message); - } - } - if (messagesToSearch.isEmpty()) { - continue; - } - } - if (listener != null) { - listener.listLocalMessagesStarted(account, null); - } - - if (integrate || displayableOnly || folderNames != null || noSpecialFolders) { - List tmpFoldersToSearch = new LinkedList(); - try { - LocalStore store = account.getLocalStore(); - List folders = store.getPersonalNamespaces(false); - Set folderNameSet = null; - if (folderNames != null) { - folderNameSet = new HashSet(); - folderNameSet.addAll(Arrays.asList(folderNames)); - } - for (Folder folder : folders) { - LocalFolder localFolder = (LocalFolder)folder; - boolean include = true; - folder.refresh(prefs); - String localFolderName = localFolder.getName(); - if (integrate) { - include = localFolder.isIntegrate(); - } else { - if (folderNameSet != null) { - if (!folderNameSet.contains(localFolderName)) - - { - include = false; - } - } - // Never exclude the INBOX (see issue 1817) - else if (noSpecialFolders && !localFolderName.equalsIgnoreCase(account.getInboxFolderName()) && - !localFolderName.equals(account.getArchiveFolderName()) && account.isSpecialFolder(localFolderName)) { - include = false; - } else if (displayableOnly && modeMismatch(account.getFolderDisplayMode(), folder.getDisplayClass())) { - include = false; - } - } - - if (include) { - tmpFoldersToSearch.add(localFolder); - } - } - if (tmpFoldersToSearch.size() < 1) { - continue; - } - foldersToSearch = tmpFoldersToSearch; - } catch (MessagingException me) { - Log.e(K9.LOG_TAG, "Unable to restrict search folders in Account " + account.getDescription() + ", searching all", me); - addErrorMessage(account, null, me); - } - - } - + final HashSet uuidSet = new HashSet(Arrays.asList(search.getAccountUuids())); + Account[] accounts = Preferences.getPreferences(mApplication.getApplicationContext()).getAccounts(); + boolean allAccounts = uuidSet.contains(SearchSpecification.ALL_ACCOUNTS); + + // for every account we want to search do the query in the localstore + for (final Account account : accounts) { + + if (!allAccounts && !uuidSet.contains(account.getUuid())) { + continue; + } + + // Collecting statistics of the search result MessageRetrievalListener retrievalListener = new MessageRetrievalListener() { @Override public void messageStarted(String message, int number, int ofTotal) {} @Override + public void messagesFinished(int number) {} + @Override public void messageFinished(Message message, int number, int ofTotal) { if (!isMessageSuppressed(message.getFolder().getAccount(), message.getFolder().getName(), message)) { List messages = new ArrayList(); @@ -760,22 +664,18 @@ public class MessagingController implements Runnable { listener.listLocalMessagesAddMessages(account, null, messages); } } - - } - @Override - public void messagesFinished(int number) { - } }; - + + // alert everyone the search has started + if (listener != null) { + listener.listLocalMessagesStarted(account, null); + } + + // build and do the query in the localstore try { - String[] queryFields = {"html_content", "subject", "sender_list"}; - LocalStore localStore = account.getLocalStore(); - localStore.searchForMessages(retrievalListener, queryFields - , query, foldersToSearch, - messagesToSearch == null ? null : messagesToSearch.toArray(EMPTY_MESSAGE_ARRAY), - requiredFlags, forbiddenFlags); - + LocalStore localStore = account.getLocalStore(); + localStore.searchForMessages(retrievalListener, search); } catch (Exception e) { if (listener != null) { listener.listLocalMessagesFailed(account, null, e.getMessage()); @@ -786,7 +686,9 @@ public class MessagingController implements Runnable { listener.listLocalMessagesFinished(account, null); } } - } + } + + // publish the total search statistics if (listener != null) { listener.searchStats(stats); } diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java index 8c42b6f48..40893169e 100644 --- a/src/com/fsck/k9/mail/store/LocalStore.java +++ b/src/com/fsck/k9/mail/store/LocalStore.java @@ -69,6 +69,9 @@ import com.fsck.k9.mail.store.LockableDatabase.DbCallback; import com.fsck.k9.mail.store.LockableDatabase.WrappedException; import com.fsck.k9.mail.store.StorageManager.StorageProvider; import com.fsck.k9.provider.AttachmentProvider; +import com.fsck.k9.search.ConditionsTreeNode; +import com.fsck.k9.search.LocalSearch; +import com.fsck.k9.search.SearchSpecification.SEARCHFIELD; /** *
@@ -658,6 +661,22 @@ public class LocalStore extends Store implements Serializable {
         return new LocalFolder(name);
     }
 
+    private long getFolderId(final String name) throws MessagingException {
+        return database.execute(false, new DbCallback() {
+            @Override
+            public Long doDbWork(final SQLiteDatabase db) {
+                Cursor cursor = null;
+                try {
+                	cursor = db.rawQuery("SELECT id FROM folders WHERE name = '" + name + "'", null);
+                    cursor.moveToFirst();
+                    return cursor.getLong(0);        
+                } finally {
+                    Utility.closeQuietly(cursor);
+                }
+            }
+        });
+    }
+    
     // TODO this takes about 260-300ms, seems slow.
     @Override
     public List  getPersonalNamespaces(boolean forceListAll) throws MessagingException {
@@ -890,97 +909,57 @@ public class LocalStore extends Store implements Serializable {
         return true;
     }
 
-    public Message[] searchForMessages(MessageRetrievalListener listener, String[] queryFields, String queryString,
-                                       List folders, Message[] messages, final Flag[] requiredFlags, final Flag[] forbiddenFlags) throws MessagingException {
-        List args = new LinkedList();
-
-        StringBuilder whereClause = new StringBuilder();
-        if (queryString != null && queryString.length() > 0) {
-            boolean anyAdded = false;
-            String likeString = "%" + queryString + "%";
-            whereClause.append(" AND (");
-            for (String queryField : queryFields) {
-
-                if (anyAdded) {
-                    whereClause.append(" OR ");
+    // TODO find beter solution
+    private static boolean isFolderId(String str) {
+        if (str == null) {
+                return false;
+        }
+        int length = str.length();
+        if (length == 0) {
+                return false;
+        }
+        int i = 0;
+        if (str.charAt(0) == '-') {
+        	return false;
+        }
+        for (; i < length; i++) {
+                char c = str.charAt(i);
+                if (c <= '/' || c >= ':') {
+                        return false;
                 }
-                whereClause.append(queryField).append(" LIKE ? ");
-                args.add(likeString);
-                anyAdded = true;
-            }
-
-
-            whereClause.append(" )");
         }
-        if (folders != null && !folders.isEmpty()) {
-            whereClause.append(" AND folder_id in (");
-            boolean anyAdded = false;
-            for (LocalFolder folder : folders) {
-                if (anyAdded) {
-                    whereClause.append(",");
-                }
-                anyAdded = true;
-                whereClause.append("?");
-                args.add(Long.toString(folder.getId()));
-            }
-            whereClause.append(" )");
-        }
-        if (messages != null && messages.length > 0) {
-            whereClause.append(" AND ( ");
-            boolean anyAdded = false;
-            for (Message message : messages) {
-                if (anyAdded) {
-                    whereClause.append(" OR ");
-                }
-                anyAdded = true;
-                whereClause.append(" ( uid = ? AND folder_id = ? ) ");
-                args.add(message.getUid());
-                args.add(Long.toString(((LocalFolder)message.getFolder()).getId()));
-            }
-            whereClause.append(" )");
-        }
-        if (forbiddenFlags != null && forbiddenFlags.length > 0) {
-            whereClause.append(" AND (");
-            boolean anyAdded = false;
-            for (Flag flag : forbiddenFlags) {
-                if (anyAdded) {
-                    whereClause.append(" AND ");
-                }
-                anyAdded = true;
-                whereClause.append(" flags NOT LIKE ?");
-
-                args.add("%" + flag.toString() + "%");
-            }
-            whereClause.append(" )");
-        }
-        if (requiredFlags != null && requiredFlags.length > 0) {
-            whereClause.append(" AND (");
-            boolean anyAdded = false;
-            for (Flag flag : requiredFlags) {
-                if (anyAdded) {
-                    whereClause.append(" OR ");
-                }
-                anyAdded = true;
-                whereClause.append(" flags LIKE ?");
-
-                args.add("%" + flag.toString() + "%");
-            }
-            whereClause.append(" )");
-        }
-
-        if (K9.DEBUG) {
-            Log.v(K9.LOG_TAG, "whereClause = " + whereClause.toString());
-            Log.v(K9.LOG_TAG, "args = " + args);
-        }
-        return getMessages(
-                   listener,
-                   null,
-                   "SELECT "
-                   + GET_MESSAGES_COLS
-                   + "FROM messages WHERE (empty IS NULL OR empty != 1) AND deleted = 0 " + whereClause.toString() + " ORDER BY date DESC"
-                   , args.toArray(EMPTY_STRING_ARRAY)
-               );
+        return true;
     }
+    
+	public Message[] searchForMessages(MessageRetrievalListener retrievalListener,
+										LocalSearch search) throws MessagingException {  
+		
+		// update some references in the search that have to be bound to this one store
+		for (ConditionsTreeNode node : search.getLeafSet()) {
+			if (node.mCondition.field == SEARCHFIELD.FOLDER) {	
+				// TODO find better solution
+				if (isFolderId(node.mCondition.value)) {
+					continue;
+				}
+				
+				if (node.mCondition.value.equals(LocalSearch.GENERIC_INBOX_NAME)) {
+					node.mCondition.value = mAccount.getInboxFolderName();
+				}
+				node.mCondition.value = String.valueOf(getFolderId(node.mCondition.value));
+			}
+		}
+
+    	// build sql query       
+        String sqlQuery = "SELECT " + GET_MESSAGES_COLS + "FROM messages WHERE deleted = 0 " 
+        				+ (search.getConditions() != null ? "AND (" + search.getConditions() + ")" : "") + " ORDER BY date DESC";
+        
+        if (K9.DEBUG) {
+            Log.d(K9.LOG_TAG, "Query = " + sqlQuery);
+        }
+        
+		return getMessages(retrievalListener, null, sqlQuery, new String[] {});
+	}
+	
     /*
      * Given a query string, actually do the query for the messages and
      * call the MessageRetrievalListener for each one

From 442805fe6237d530c8fab9a4af563acee816b877 Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 09:28:19 -0400
Subject: [PATCH 04/12] Made thread_root a searchable field of the message
 table. This can be used to display threads.

---
 src/com/fsck/k9/search/SearchSpecification.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/com/fsck/k9/search/SearchSpecification.java b/src/com/fsck/k9/search/SearchSpecification.java
index 3c422c64e..6f6a38fa5 100644
--- a/src/com/fsck/k9/search/SearchSpecification.java
+++ b/src/com/fsck/k9/search/SearchSpecification.java
@@ -90,7 +90,7 @@ public interface SearchSpecification extends Parcelable {
         SUBJECT("subject"), DATE("date"), UID("uid"), FLAG("flags"),
         SENDER("sender_list"), TO("to_list"), CC("cc_list"), FOLDER("folder_id"),
         BCC("bcc_list"), REPLY_TO("reply_to_list"), MESSAGE("text_content"),
-        ATTACHMENT_COUNT("attachment_count"), DELETED("deleted");
+        ATTACHMENT_COUNT("attachment_count"), DELETED("deleted"), THREAD_ROOT("thread_root");
 
         private String dbName;
         

From 9883148b2f48f7854d2871ffc3d827f942f89f81 Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 10:03:19 -0400
Subject: [PATCH 05/12] Fixed wrong construction of leaf sets and unrightfully
 ignored exceptions.

---
 src/com/fsck/k9/search/LocalSearch.java | 64 ++++++++++++-------------
 1 file changed, 32 insertions(+), 32 deletions(-)

diff --git a/src/com/fsck/k9/search/LocalSearch.java b/src/com/fsck/k9/search/LocalSearch.java
index 48ca3ca7c..6b07459c4 100644
--- a/src/com/fsck/k9/search/LocalSearch.java
+++ b/src/com/fsck/k9/search/LocalSearch.java
@@ -153,8 +153,13 @@ public class LocalSearch implements SearchSpecification {
 	 * @return New top AND node, new root.
 	 */
 	public ConditionsTreeNode and(SearchCondition condition) {
-		ConditionsTreeNode tmp = new ConditionsTreeNode(condition);
-		return and(tmp);
+		try {
+			ConditionsTreeNode tmp = new ConditionsTreeNode(condition);
+			return and(tmp);
+		} catch (Exception e) {
+			// impossible
+			return null;
+		}
 	}
 	
 	/**
@@ -163,22 +168,17 @@ public class LocalSearch implements SearchSpecification {
 	 * 
 	 * @param node Node to 'AND' with.
 	 * @return New top AND node, new root.
+	 * @throws Exception 
 	 */
-	public ConditionsTreeNode and(ConditionsTreeNode node) {
-		try {
-			mLeafSet.add(node);
-			
-			if (mConditions == null) {
-				mConditions = node;
-				return node;
-			}
-			
-			mConditions = mConditions.and(node);
-			return mConditions;
-		} catch (Exception e) {
-			// IMPOSSIBLE!
-			return null;
+	public ConditionsTreeNode and(ConditionsTreeNode node) throws Exception {
+		mLeafSet.addAll(node.getLeafSet());
+		
+		if (mConditions == null) {
+			mConditions = node;
+			return node;
 		}
+		
+		return mConditions.and(node);
 	}
 	
 	/**
@@ -189,8 +189,13 @@ public class LocalSearch implements SearchSpecification {
 	 * @return New top OR node, new root.
 	 */
 	public ConditionsTreeNode or(SearchCondition condition) {
-		ConditionsTreeNode tmp = new ConditionsTreeNode(condition);
-		return or(tmp);
+		try {
+			ConditionsTreeNode tmp = new ConditionsTreeNode(condition);
+			return or(tmp);
+		} catch (Exception e) {
+			// impossible
+			return null;
+		}
 	}
 	
 	/**
@@ -199,22 +204,17 @@ public class LocalSearch implements SearchSpecification {
 	 * 
 	 * @param node Node to 'OR' with.
 	 * @return New top OR node, new root.
+	 * @throws Exception 
 	 */
-	public ConditionsTreeNode or(ConditionsTreeNode node) {
-		try {
-			mLeafSet.add(node);
-			
-			if (mConditions == null) {
-				mConditions = node;
-				return node;
-			}
-			
-			mConditions = mConditions.or(node);
-			return mConditions;
-		} catch (Exception e) {
-			// IMPOSSIBLE!
-			return null;
+	public ConditionsTreeNode or(ConditionsTreeNode node) throws Exception {
+		mLeafSet.addAll(node.getLeafSet());
+		
+		if (mConditions == null) {
+			mConditions = node;
+			return node;
 		}
+		
+		return mConditions.or(node);
 	}
 	
 	/**

From bdfc9d685283b99ecd7cc76502401d621c8a4865 Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 10:08:39 -0400
Subject: [PATCH 06/12] Added two convenience methods to avoid dealing with
 exceptions all the time.

---
 .../fsck/k9/search/ConditionsTreeNode.java    | 36 +++++++++++++++++++
 1 file changed, 36 insertions(+)

diff --git a/src/com/fsck/k9/search/ConditionsTreeNode.java b/src/com/fsck/k9/search/ConditionsTreeNode.java
index fbb31a7b9..650755a7d 100644
--- a/src/com/fsck/k9/search/ConditionsTreeNode.java
+++ b/src/com/fsck/k9/search/ConditionsTreeNode.java
@@ -141,6 +141,24 @@ public class ConditionsTreeNode implements Parcelable{
 		return add(expr, OPERATOR.AND);
 	}
 	
+	/**
+	 * Convenience method.
+	 * Adds the provided condition as the second argument of an AND 
+	 * clause to this node.
+	 * 
+	 * @param condition Condition to 'AND' with.
+	 * @return New top AND node, new root.
+	 */
+	public ConditionsTreeNode and(SearchCondition condition) {
+		try {
+			ConditionsTreeNode tmp = new ConditionsTreeNode(condition);
+			return and(tmp);
+		} catch (Exception e) {
+			// impossible
+			return null;
+		}
+	}
+	
 	/**
 	 * Adds the expression as the second argument of an OR 
 	 * clause to this node.
@@ -153,6 +171,24 @@ public class ConditionsTreeNode implements Parcelable{
 		return add(expr, OPERATOR.OR);
 	}
 	
+	/**
+	 * Convenience method.
+	 * Adds the provided condition as the second argument of an OR 
+	 * clause to this node.
+	 * 
+	 * @param condition Condition to 'OR' with.
+	 * @return New top OR node, new root.
+	 */
+	public ConditionsTreeNode or(SearchCondition condition) {
+		try {
+			ConditionsTreeNode tmp = new ConditionsTreeNode(condition);
+			return or(tmp);
+		} catch (Exception e) {
+			// impossible
+			return null;
+		}
+	}
+	
 	/**
 	 * This applies the MPTT labeling to the subtree of which this node
 	 * is the root node.

From 235e1f913b8431a0d0efe080c29ebc0e1ca92456 Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 10:40:13 -0400
Subject: [PATCH 07/12] Avoid needless nullpointers.

---
 src/com/fsck/k9/search/LocalSearch.java | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/com/fsck/k9/search/LocalSearch.java b/src/com/fsck/k9/search/LocalSearch.java
index 6b07459c4..a9bff7348 100644
--- a/src/com/fsck/k9/search/LocalSearch.java
+++ b/src/com/fsck/k9/search/LocalSearch.java
@@ -298,7 +298,7 @@ public class LocalSearch implements SearchSpecification {
      * @return Name of the search.
      */
     public String getName() {
-        return mName;
+        return (mName == null ? "" : mName);
     }
 	
     /**

From f550aaefb58bb41ae4b5a358a63b07a7dccc273e Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 14:03:40 -0400
Subject: [PATCH 08/12] Add new SearchAccount class and move it together with
 SearchModifier to the search package ( refactor ).

---
 src/com/fsck/k9/SearchAccount.java            | 153 ------------------
 src/com/fsck/k9/activity/AccountList.java     |   2 +-
 src/com/fsck/k9/activity/FolderList.java      |   1 +
 src/com/fsck/k9/provider/MessageProvider.java |   2 +-
 src/com/fsck/k9/search/SearchAccount.java     |  66 ++++++++
 .../{activity => search}/SearchModifier.java  |   4 +-
 6 files changed, 71 insertions(+), 157 deletions(-)
 delete mode 100644 src/com/fsck/k9/SearchAccount.java
 create mode 100644 src/com/fsck/k9/search/SearchAccount.java
 rename src/com/fsck/k9/{activity => search}/SearchModifier.java (90%)

diff --git a/src/com/fsck/k9/SearchAccount.java b/src/com/fsck/k9/SearchAccount.java
deleted file mode 100644
index 97e2092ee..000000000
--- a/src/com/fsck/k9/SearchAccount.java
+++ /dev/null
@@ -1,153 +0,0 @@
-package com.fsck.k9;
-
-import java.io.Serializable;
-import java.util.UUID;
-
-import android.content.Context;
-
-import com.fsck.k9.mail.Flag;
-import com.fsck.k9.search.SearchSpecification;
-
-/**
- * This is a meta-Account that represents one or more accounts with filters on them.  The filter specification
- * is defined by {@link com.fsck.k9.activity.SearchModifier}.
- */
-public class SearchAccount implements BaseAccount, SearchSpecification, Serializable {
-    /**
-     * Create a {@code SearchAccount} instance for the Unified Inbox.
-     *
-     * @param context
-     *         A {@link Context} instance that will be used to get localized strings and will be
-     *         passed on to the {@code SearchAccount} instance.
-     *
-     * @return The {@link SearchAccount} instance for the Unified Inbox.
-     */
-    public static SearchAccount createUnifiedInboxAccount(Context context) {
-        SearchAccount unifiedInbox = new SearchAccount(context, true, null, null);
-        unifiedInbox.setDescription(context.getString(R.string.integrated_inbox_title));
-        unifiedInbox.setEmail(context.getString(R.string.integrated_inbox_detail));
-        return unifiedInbox;
-    }
-
-    /**
-     * Create a {@code SearchAccount} instance for the special account "All messages".
-     *
-     * @param context
-     *         A {@link Context} instance that will be used to get localized strings and will be
-     *         passed on to the {@code SearchAccount} instance.
-     *
-     * @return The {@link SearchAccount} instance for the Unified Inbox.
-     */
-    public static SearchAccount createAllMessagesAccount(Context context) {
-        SearchAccount allMessages = new SearchAccount(context, false, null, null);
-        allMessages.setDescription(context.getString(R.string.search_all_messages_title));
-        allMessages.setEmail(context.getString(R.string.search_all_messages_detail));
-        return allMessages;
-    }
-
-
-    private static final long serialVersionUID = -4388420303235543976L;
-    private Flag[] mRequiredFlags = null;
-    private Flag[] mForbiddenFlags = null;
-    private String email = null;
-    private String description = null;
-    private String query = "";
-    private boolean integrate = false;
-    private String mUuid = null;
-    private boolean builtin = false;
-    private String[] accountUuids = null;
-    private String[] folderNames = null;
-
-    public SearchAccount(Preferences preferences) {
-    }
-
-    protected synchronized void delete(Preferences preferences) {
-    }
-
-    public synchronized void save(Preferences preferences) {
-    }
-
-    public SearchAccount(Context context, boolean nintegrate, Flag[] requiredFlags, Flag[] forbiddenFlags) {
-        mRequiredFlags = requiredFlags;
-        mForbiddenFlags = forbiddenFlags;
-        integrate = nintegrate;
-    }
-
-    @Override
-    public synchronized String getEmail() {
-        return email;
-    }
-
-    @Override
-    public synchronized void setEmail(String email) {
-        this.email = email;
-    }
-
-    public Flag[] getRequiredFlags() {
-        return mRequiredFlags;
-    }
-
-    public Flag[] getForbiddenFlags() {
-        return mForbiddenFlags;
-    }
-
-    public boolean isIntegrate() {
-        return integrate;
-    }
-
-    public String getDescription() {
-        return description;
-    }
-
-    public void setDescription(String description) {
-        this.description = description;
-    }
-
-    public String getQuery() {
-        return query;
-    }
-
-    public void setQuery(String query) {
-        this.query = query;
-    }
-
-    public String getUuid() {
-        if (mUuid == null) {
-            setUuid(UUID.randomUUID().toString());
-        }
-        return mUuid;
-    }
-
-    public void setUuid(String nUuid) {
-        mUuid = nUuid;
-    }
-
-    public void setIntegrate(boolean integrate) {
-        this.integrate = integrate;
-    }
-
-    public boolean isBuiltin() {
-        return builtin;
-    }
-
-    public void setBuiltin(boolean builtin) {
-        this.builtin = builtin;
-    }
-
-    public String[] getAccountUuids() {
-        return accountUuids;
-    }
-
-    public void setAccountUuids(String[] accountUuids) {
-        this.accountUuids = accountUuids;
-    }
-
-    @Override
-    public String[] getFolderNames() {
-        return folderNames;
-    }
-
-    public void setFolderNames(String[] folderNames) {
-        this.folderNames = folderNames;
-    }
-}
diff --git a/src/com/fsck/k9/activity/AccountList.java b/src/com/fsck/k9/activity/AccountList.java
index 52c64dbe3..9c17ffd42 100644
--- a/src/com/fsck/k9/activity/AccountList.java
+++ b/src/com/fsck/k9/activity/AccountList.java
@@ -21,7 +21,7 @@ import com.fsck.k9.FontSizes;
 import com.fsck.k9.K9;
 import com.fsck.k9.Preferences;
 import com.fsck.k9.R;
-import com.fsck.k9.SearchAccount;
+import com.fsck.k9.search.SearchAccount;
 
 
 /**
diff --git a/src/com/fsck/k9/activity/FolderList.java b/src/com/fsck/k9/activity/FolderList.java
index dc034c4ba..e4c2a3e2a 100644
--- a/src/com/fsck/k9/activity/FolderList.java
+++ b/src/com/fsck/k9/activity/FolderList.java
@@ -67,6 +67,7 @@ import com.fsck.k9.mail.Folder;
 import com.fsck.k9.mail.Message;
 import com.fsck.k9.mail.MessagingException;
 import com.fsck.k9.mail.store.LocalStore.LocalFolder;
+import com.fsck.k9.search.SearchModifier;
 import com.fsck.k9.search.SearchSpecification;
 import com.fsck.k9.service.MailService;
 
diff --git a/src/com/fsck/k9/provider/MessageProvider.java b/src/com/fsck/k9/provider/MessageProvider.java
index 1e351d057..05428b6cb 100644
--- a/src/com/fsck/k9/provider/MessageProvider.java
+++ b/src/com/fsck/k9/provider/MessageProvider.java
@@ -21,7 +21,6 @@ import com.fsck.k9.Account;
 import com.fsck.k9.AccountStats;
 import com.fsck.k9.K9;
 import com.fsck.k9.Preferences;
-import com.fsck.k9.SearchAccount;
 import com.fsck.k9.activity.FolderInfoHolder;
 import com.fsck.k9.activity.MessageInfoHolder;
 import com.fsck.k9.activity.MessageList;
@@ -34,6 +33,7 @@ import com.fsck.k9.mail.Folder;
 import com.fsck.k9.mail.Message;
 import com.fsck.k9.mail.MessagingException;
 import com.fsck.k9.mail.store.LocalStore;
+import com.fsck.k9.search.SearchAccount;
 
 import java.lang.ref.WeakReference;
 import java.util.ArrayList;
diff --git a/src/com/fsck/k9/search/SearchAccount.java b/src/com/fsck/k9/search/SearchAccount.java
new file mode 100644
index 000000000..d58c62de1
--- /dev/null
+++ b/src/com/fsck/k9/search/SearchAccount.java
@@ -0,0 +1,66 @@
+package com.fsck.k9.search;
+
+import java.util.UUID;
+
+import com.fsck.k9.BaseAccount;
+
+/**
+ * This class is basically a wrapper around a LocalSearch. It allows to expose it as
+ * an account. This is a meta-account containing all the e-mail that matches the search.
+ */
+public class SearchAccount implements BaseAccount {
+
+    private String mEmail = null;
+    private String mDescription = null;
+    private LocalSearch mSearch = null;
+    private String mFakeUuid = null;
+    
+    public SearchAccount(LocalSearch search, String description, String email) throws IllegalArgumentException{
+    	if (search == null) {
+    		throw new IllegalArgumentException("Provided LocalSearch was null");
+    	}
+    	
+    	this.mSearch = search;
+    	this.mDescription = description;
+    	this.mEmail = email;
+    }
+
+    @Override
+    public synchronized String getEmail() {
+        return mEmail;
+    }
+
+    @Override
+    public synchronized void setEmail(String email) {
+        this.mEmail = email;
+    }
+    
+    @Override
+    public String getDescription() {
+        return mDescription;
+    }
+    
+    @Override
+    public void setDescription(String description) {
+        this.mDescription = description;
+    }   
+
+    public LocalSearch getRelatedSearch() {
+    	return mSearch;
+    }
+    
+    @Override
+    /*
+     * This will only be used when accessed as an Account. If that
+     * is the case we don't want to return the uuid of a real account since 
+     * this is posing as a fake meta-account. If this object is accesed as
+     * a Search then methods from LocalSearch will be called which do handle 
+     * things nice.
+     */
+    public String getUuid() {
+    	if (mFakeUuid == null){
+    		mFakeUuid = UUID.randomUUID().toString();
+    	}
+    	return mFakeUuid;
+    }
+}
diff --git a/src/com/fsck/k9/activity/SearchModifier.java b/src/com/fsck/k9/search/SearchModifier.java
similarity index 90%
rename from src/com/fsck/k9/activity/SearchModifier.java
rename to src/com/fsck/k9/search/SearchModifier.java
index 8b610ff6e..536fee51a 100644
--- a/src/com/fsck/k9/activity/SearchModifier.java
+++ b/src/com/fsck/k9/search/SearchModifier.java
@@ -1,10 +1,10 @@
-package com.fsck.k9.activity;
+package com.fsck.k9.search;
 
 import com.fsck.k9.R;
 import com.fsck.k9.mail.Flag;
 
 /**
- * This enum represents filtering parameters used by {@link com.fsck.k9.SearchAccount}.
+ * This enum represents filtering parameters used by {@link com.fsck.k9.search.SearchAccount}.
  */
 enum SearchModifier {
     FLAGGED(R.string.flagged_modifier, new Flag[]{Flag.FLAGGED}, null),

From a3d227649fc0ec8bb8f4290bbec8857cddafea80 Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 14:12:52 -0400
Subject: [PATCH 09/12] Change visibility of SearchModifier class and members.

---
 src/com/fsck/k9/search/SearchModifier.java | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/com/fsck/k9/search/SearchModifier.java b/src/com/fsck/k9/search/SearchModifier.java
index 536fee51a..f027ab062 100644
--- a/src/com/fsck/k9/search/SearchModifier.java
+++ b/src/com/fsck/k9/search/SearchModifier.java
@@ -6,13 +6,13 @@ import com.fsck.k9.mail.Flag;
 /**
  * This enum represents filtering parameters used by {@link com.fsck.k9.search.SearchAccount}.
  */
-enum SearchModifier {
+public enum SearchModifier {
     FLAGGED(R.string.flagged_modifier, new Flag[]{Flag.FLAGGED}, null),
     UNREAD(R.string.unread_modifier, null, new Flag[]{Flag.SEEN});
 
-    final int resId;
-    final Flag[] requiredFlags;
-    final Flag[] forbiddenFlags;
+    public final int resId;
+    public final Flag[] requiredFlags;
+    public final Flag[] forbiddenFlags;
 
     SearchModifier(int nResId, Flag[] nRequiredFlags, Flag[] nForbiddenFlags) {
         resId = nResId;

From f01f2f15cde94825288db6cd6fe9f314b0c47ab1 Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 14:19:50 -0400
Subject: [PATCH 10/12] Add static methods to create unifiedInbox and
 allMessages accounts.

---
 src/com/fsck/k9/search/SearchAccount.java | 21 +++++++++++++++++++++
 1 file changed, 21 insertions(+)

diff --git a/src/com/fsck/k9/search/SearchAccount.java b/src/com/fsck/k9/search/SearchAccount.java
index d58c62de1..d76a8a0dd 100644
--- a/src/com/fsck/k9/search/SearchAccount.java
+++ b/src/com/fsck/k9/search/SearchAccount.java
@@ -2,7 +2,10 @@ package com.fsck.k9.search;
 
 import java.util.UUID;
 
+import android.content.Context;
+
 import com.fsck.k9.BaseAccount;
+import com.fsck.k9.R;
 
 /**
  * This class is basically a wrapper around a LocalSearch. It allows to expose it as
@@ -10,6 +13,24 @@ import com.fsck.k9.BaseAccount;
  */
 public class SearchAccount implements BaseAccount {
 
+    // create the all messages search ( all accounts is default when none specified )
+	public static SearchAccount createAllMessagesAccount(Context context) {
+        String name = context.getString(R.string.search_all_messages_title);
+        LocalSearch tmpSearch = new LocalSearch(name);
+        return new SearchAccount(tmpSearch, name, 
+        		context.getString(R.string.search_all_messages_detail));
+	}
+	
+
+	// create the unified inbox meta account ( all accounts is default when none specified )
+	public static SearchAccount createUnifiedInboxAccount(Context context) {
+        String name = context.getString(R.string.integrated_inbox_title);
+        LocalSearch tmpSearch = new LocalSearch(name);
+        tmpSearch.addAllowedFolder(SearchSpecification.GENERIC_INBOX_NAME);
+        return new SearchAccount(tmpSearch, name,
+        		context.getString(R.string.integrated_inbox_detail));
+	}
+	
     private String mEmail = null;
     private String mDescription = null;
     private LocalSearch mSearch = null;

From bf82d0af7cbb17ab1c2c2e74fffe3748c7a77e05 Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 15:06:57 -0400
Subject: [PATCH 11/12] Changed all the calls to MessageList to work using the
 new LocalSearch class. These are all tested and working changes.

---
 src/com/fsck/k9/activity/Accounts.java        |  80 ++++++--------
 src/com/fsck/k9/activity/FolderList.java      | 100 +++++-------------
 .../fsck/k9/activity/LauncherShortcuts.java   |   3 +-
 .../k9/controller/MessagingController.java    |  14 ++-
 src/com/fsck/k9/provider/MessageProvider.java |   2 +-
 .../k9/provider/UnreadWidgetProvider.java     |   7 +-
 6 files changed, 76 insertions(+), 130 deletions(-)

diff --git a/src/com/fsck/k9/activity/Accounts.java b/src/com/fsck/k9/activity/Accounts.java
index cbaaaa8ef..596801554 100644
--- a/src/com/fsck/k9/activity/Accounts.java
+++ b/src/com/fsck/k9/activity/Accounts.java
@@ -62,7 +62,6 @@ import com.fsck.k9.FontSizes;
 import com.fsck.k9.K9;
 import com.fsck.k9.Preferences;
 import com.fsck.k9.R;
-import com.fsck.k9.SearchAccount;
 import com.fsck.k9.activity.misc.ExtendedAsyncTask;
 import com.fsck.k9.activity.misc.NonConfigurationInstance;
 import com.fsck.k9.activity.setup.AccountSettings;
@@ -79,6 +78,9 @@ import com.fsck.k9.mail.Transport;
 import com.fsck.k9.mail.internet.MimeUtility;
 import com.fsck.k9.mail.store.StorageManager;
 import com.fsck.k9.mail.store.WebDavStore;
+import com.fsck.k9.search.LocalSearch;
+import com.fsck.k9.search.SearchAccount;
+import com.fsck.k9.search.SearchModifier;
 import com.fsck.k9.search.SearchSpecification;
 import com.fsck.k9.view.ColorChip;
 import com.fsck.k9.preferences.SettingsExporter;
@@ -424,8 +426,18 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
      * Creates and initializes the special accounts ('Unified Inbox' and 'All Messages')
      */
     private void createSpecialAccounts() {
-        integratedInboxAccount = SearchAccount.createUnifiedInboxAccount(this);
-        unreadAccount = SearchAccount.createAllMessagesAccount(this);
+    	// create the unified inbox meta account ( all accounts is default when none specified )
+        String name = getString(R.string.integrated_inbox_title);
+        LocalSearch tmpSearch = new LocalSearch(name);
+        tmpSearch.addAllowedFolder(SearchSpecification.GENERIC_INBOX_NAME);
+        integratedInboxAccount = new SearchAccount(tmpSearch, name,
+        		getString(R.string.integrated_inbox_detail));
+        
+        // create the all messages search ( all accounts is default when none specified )
+        name = getString(R.string.search_all_messages_title);
+        tmpSearch = new LocalSearch(name);
+        unreadAccount = new SearchAccount(tmpSearch, name, 
+        		getString(R.string.search_all_messages_detail)); 	
     }
 
     @SuppressWarnings("unchecked")
@@ -550,7 +562,8 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
                 pendingWork.put(account, "true");
                 final SearchAccount searchAccount = (SearchAccount)account;
 
-                MessagingController.getInstance(getApplication()).searchLocalMessages(searchAccount, null, new MessagingListener() {
+                MessagingController.getInstance(getApplication())
+                	.searchLocalMessages(searchAccount.getRelatedSearch(), new MessagingListener() {
                     @Override
                     public void searchStats(AccountStats stats) {
                         mListener.accountStatusChanged(searchAccount, stats);
@@ -607,7 +620,7 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
     private boolean onOpenAccount(BaseAccount account) {
         if (account instanceof SearchAccount) {
             SearchAccount searchAccount = (SearchAccount)account;
-            MessageList.actionHandle(this, searchAccount.getDescription(), searchAccount);
+            MessageList.actionDisplaySearch(this, searchAccount.getRelatedSearch(), false);
         } else {
             Account realAccount = (Account)account;
             if (!realAccount.isEnabled()) {
@@ -624,8 +637,10 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
             if (K9.FOLDER_NONE.equals(realAccount.getAutoExpandFolderName())) {
                 FolderList.actionHandleAccount(this, realAccount);
             } else {
-                MessageList.actionHandleFolder(this, realAccount, realAccount.getAutoExpandFolderName());
-            }
+            	LocalSearch search = new LocalSearch(realAccount.getAutoExpandFolderName());
+            	search.addAllowedFolder(realAccount.getAutoExpandFolderName());
+            	search.addAccountUuid(realAccount.getUuid());
+                MessageList.actionDisplaySearch(this, search, true);}
         }
         return true;
     }
@@ -1769,49 +1784,20 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
         }
         @Override
         public void onClick(View v) {
-            String description = getString(R.string.search_title, account.getDescription(), getString(searchModifier.resId));
+            final String description = getString(R.string.search_title, account.getDescription(), getString(searchModifier.resId));
+            LocalSearch search = null;
+            
             if (account instanceof SearchAccount) {
-                SearchAccount searchAccount = (SearchAccount)account;
-
-                MessageList.actionHandle(Accounts.this,
-                                         description, "", searchAccount.isIntegrate(),
-                                         combine(searchAccount.getRequiredFlags(), searchModifier.requiredFlags),
-                                         combine(searchAccount.getForbiddenFlags(), searchModifier.forbiddenFlags));
+            	search = ((SearchAccount) account).getRelatedSearch();
+            	search.setName(description);
             } else {
-                SearchSpecification searchSpec = new SearchSpecification() {
-                    @Override
-                    public String[] getAccountUuids() {
-                        return new String[] { account.getUuid() };
-                    }
-
-                    @Override
-                    public Flag[] getForbiddenFlags() {
-                        return searchModifier.forbiddenFlags;
-                    }
-
-                    @Override
-                    public String getQuery() {
-                        return "";
-                    }
-
-                    @Override
-                    public Flag[] getRequiredFlags() {
-                        return searchModifier.requiredFlags;
-                    }
-
-                    @Override
-                    public boolean isIntegrate() {
-                        return false;
-                    }
-
-                    @Override
-                    public String[] getFolderNames() {
-                        return null;
-                    }
-
-                };
-                MessageList.actionHandle(Accounts.this, description, searchSpec);
+            	search = new LocalSearch(description);
+            	search.addAccountUuid(account.getUuid());
             }
+            
+			search.allRequiredFlags(searchModifier.requiredFlags);
+			search.allForbiddenFlags(searchModifier.forbiddenFlags);
+        	MessageList.actionDisplaySearch(Accounts.this, search, false);
         }
 
     }
diff --git a/src/com/fsck/k9/activity/FolderList.java b/src/com/fsck/k9/activity/FolderList.java
index e4c2a3e2a..6c8477833 100644
--- a/src/com/fsck/k9/activity/FolderList.java
+++ b/src/com/fsck/k9/activity/FolderList.java
@@ -67,6 +67,7 @@ import com.fsck.k9.mail.Folder;
 import com.fsck.k9.mail.Message;
 import com.fsck.k9.mail.MessagingException;
 import com.fsck.k9.mail.store.LocalStore.LocalFolder;
+import com.fsck.k9.search.LocalSearch;
 import com.fsck.k9.search.SearchModifier;
 import com.fsck.k9.search.SearchSpecification;
 import com.fsck.k9.service.MailService;
@@ -621,7 +622,10 @@ public class FolderList extends K9ListActivity implements OnNavigationListener {
     }
 
     private void onOpenFolder(String folder) {
-        MessageList.actionHandleFolder(this, mAccount, folder);
+    	LocalSearch search = new LocalSearch(folder);
+    	search.addAccountUuid(mAccount.getUuid());
+    	search.addAllowedFolder(folder);
+        MessageList.actionDisplaySearch(this, search, false);
     }
 
     private void onCompact(Account account) {
@@ -1258,86 +1262,34 @@ public class FolderList extends K9ListActivity implements OnNavigationListener {
         }
         @Override
         public void onClick(View v) {
-            String description = getString(R.string.search_title,
+            final String description = getString(R.string.search_title,
                                            getString(R.string.message_list_title, account.getDescription(), displayName),
                                            getString(searchModifier.resId));
-
-            SearchSpecification searchSpec = new SearchSpecification() {
-                @Override
-                public String[] getAccountUuids() {
-                    return new String[] { account.getUuid() };
-                }
-
-                @Override
-                public Flag[] getForbiddenFlags() {
-                    return searchModifier.forbiddenFlags;
-                }
-
-                @Override
-                public String getQuery() {
-                    return "";
-                }
-
-                @Override
-                public Flag[] getRequiredFlags() {
-                    return searchModifier.requiredFlags;
-                }
-
-                @Override
-                public boolean isIntegrate() {
-                    return false;
-                }
-
-                @Override
-                public String[] getFolderNames() {
-                    return new String[] { folderName };
-                }
-
-            };
-            MessageList.actionHandle(FolderList.this, description, searchSpec);
-
+            
+        	LocalSearch search = new LocalSearch(description);
+        	try {
+				search.allRequiredFlags(searchModifier.requiredFlags);
+	        	search.allForbiddenFlags(searchModifier.forbiddenFlags);
+			} catch (Exception e) {
+				// TODO Auto-generated catch block
+				e.printStackTrace();
+			}
+        	search.addAllowedFolder(folderName);
+        	search.addAccountUuid(account.getUuid());
+        	MessageList.actionDisplaySearch(FolderList.this, search, false);
         }
-
     }
 
-    private static Flag[] UNREAD_FLAG_ARRAY = { Flag.SEEN };
-
     private void openUnreadSearch(Context context, final Account account) {
         String description = getString(R.string.search_title, mAccount.getDescription(), getString(R.string.unread_modifier));
-
-        SearchSpecification searchSpec = new SearchSpecification() {
-            //interface has no override            @Override
-            public String[] getAccountUuids() {
-                return new String[] { account.getUuid() };
-            }
-
-            //interface has no override            @Override
-            public Flag[] getForbiddenFlags() {
-                return UNREAD_FLAG_ARRAY;
-            }
-
-            //interface has no override            @Override
-            public String getQuery() {
-                return "";
-            }
-
-            @Override
-            public Flag[] getRequiredFlags() {
-                return null;
-            }
-
-            @Override
-            public boolean isIntegrate() {
-                return false;
-            }
-
-            @Override
-            public String[] getFolderNames() {
-                return null;
-            }
-
-        };
-        MessageList.actionHandle(context, description, searchSpec);
+        LocalSearch search = new LocalSearch(description);
+        search.addAccountUuid(account.getUuid());
+        try {
+			search.allRequiredFlags(new Flag[]{Flag.SEEN});
+		} catch (Exception e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
     }
 
 }
diff --git a/src/com/fsck/k9/activity/LauncherShortcuts.java b/src/com/fsck/k9/activity/LauncherShortcuts.java
index add1d837a..f302051dc 100644
--- a/src/com/fsck/k9/activity/LauncherShortcuts.java
+++ b/src/com/fsck/k9/activity/LauncherShortcuts.java
@@ -31,8 +31,7 @@ public class LauncherShortcuts extends AccountList {
         Intent shortcutIntent = null;
 
         if (account instanceof SearchSpecification) {
-            shortcutIntent = MessageList.actionHandleAccountIntent(this, account.getDescription(),
-                    (SearchSpecification) account);
+            shortcutIntent = MessageList.intentDisplaySearch(this, (SearchSpecification) account, true, true);
         } else {
             shortcutIntent = FolderList.actionHandleAccountIntent(this, (Account) account, null,
                     true);
diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java
index dfb638828..181cb0177 100644
--- a/src/com/fsck/k9/controller/MessagingController.java
+++ b/src/com/fsck/k9/controller/MessagingController.java
@@ -3160,8 +3160,11 @@ public class MessagingController implements Runnable {
         builder.setContentTitle(mApplication.getString(R.string.notification_bg_send_title));
         builder.setContentText(account.getDescription());
 
-        Intent intent = MessageList.actionHandleFolderIntent(mApplication, account,
-                account.getInboxFolderName());
+        LocalSearch search = new LocalSearch(account.getInboxFolderName());
+        search.addAllowedFolder(account.getInboxFolderName());
+        search.addAccountUuid(account.getUuid());
+        Intent intent = MessageList.intentDisplaySearch(mApplication, search, true, true);
+        
         PendingIntent pi = PendingIntent.getActivity(mApplication, 0, intent, 0);
         builder.setContentIntent(pi);
 
@@ -3243,8 +3246,11 @@ public class MessagingController implements Runnable {
                 mApplication.getString(R.string.notification_bg_title_separator) +
                 folder.getName());
 
-        Intent intent = MessageList.actionHandleFolderIntent(mApplication, account,
-                account.getInboxFolderName());
+        LocalSearch search = new LocalSearch(account.getInboxFolderName());
+        search.addAllowedFolder(account.getInboxFolderName());
+        search.addAccountUuid(account.getUuid());
+        Intent intent = MessageList.intentDisplaySearch(mApplication, search, true, true);
+        
         PendingIntent pi = PendingIntent.getActivity(mApplication, 0, intent, 0);
         builder.setContentIntent(pi);
 
diff --git a/src/com/fsck/k9/provider/MessageProvider.java b/src/com/fsck/k9/provider/MessageProvider.java
index 05428b6cb..d40ed23e0 100644
--- a/src/com/fsck/k9/provider/MessageProvider.java
+++ b/src/com/fsck/k9/provider/MessageProvider.java
@@ -302,7 +302,7 @@ public class MessageProvider extends ContentProvider {
             final SearchAccount integratedInboxAccount = SearchAccount.createUnifiedInboxAccount(getContext());
             final MessagingController msgController = MessagingController.getInstance(K9.app);
 
-            msgController.searchLocalMessages(integratedInboxAccount, null,
+            msgController.searchLocalMessages(integratedInboxAccount.getRelatedSearch(),
                                               new MesssageInfoHolderRetrieverListener(queue));
 
             final List holders = queue.take();
diff --git a/src/com/fsck/k9/provider/UnreadWidgetProvider.java b/src/com/fsck/k9/provider/UnreadWidgetProvider.java
index 2af479d15..d8daeaa0b 100644
--- a/src/com/fsck/k9/provider/UnreadWidgetProvider.java
+++ b/src/com/fsck/k9/provider/UnreadWidgetProvider.java
@@ -8,6 +8,7 @@ import com.fsck.k9.R;
 import com.fsck.k9.activity.UnreadWidgetConfiguration;
 import com.fsck.k9.activity.FolderList;
 import com.fsck.k9.activity.MessageList;
+import com.fsck.k9.search.LocalSearch;
 
 import android.app.PendingIntent;
 import android.appwidget.AppWidgetManager;
@@ -62,8 +63,10 @@ public class UnreadWidgetProvider extends AppWidgetProvider {
                     clickIntent = FolderList.actionHandleAccountIntent(context, account, null,
                             false);
                 } else {
-                    clickIntent = MessageList.actionHandleFolderIntent(context, account,
-                            account.getAutoExpandFolderName());
+                	LocalSearch search = new LocalSearch(account.getAutoExpandFolderName());
+                	search.addAllowedFolder(account.getAutoExpandFolderName());
+                	search.addAccountUuid(account.getUuid());
+                    clickIntent = MessageList.intentDisplaySearch(context, search, true, true);
                 }
                 clickIntent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
             }

From 49197b4f1c3d599d0af773559e59fbc267d08286 Mon Sep 17 00:00:00 2001
From: Sander Bogaert 
Date: Sat, 13 Oct 2012 15:07:28 -0400
Subject: [PATCH 12/12] First attempt at making the fragmented messagelist and
 remote search play nice with the new search framework. Works partially.

---
 src/com/fsck/k9/activity/MessageList.java     | 249 ++++------
 .../fsck/k9/fragment/MessageListFragment.java | 436 ++++++++----------
 src/com/fsck/k9/search/LocalSearch.java       |  14 +
 3 files changed, 294 insertions(+), 405 deletions(-)

diff --git a/src/com/fsck/k9/activity/MessageList.java b/src/com/fsck/k9/activity/MessageList.java
index d984efdbe..aca636bbd 100644
--- a/src/com/fsck/k9/activity/MessageList.java
+++ b/src/com/fsck/k9/activity/MessageList.java
@@ -34,7 +34,11 @@ import com.fsck.k9.helper.Utility;
 import com.fsck.k9.mail.Flag;
 import com.fsck.k9.mail.Message;
 import com.fsck.k9.mail.store.StorageManager;
+import com.fsck.k9.search.LocalSearch;
 import com.fsck.k9.search.SearchSpecification;
+import com.fsck.k9.search.SearchSpecification.ATTRIBUTE;
+import com.fsck.k9.search.SearchSpecification.SEARCHFIELD;
+import com.fsck.k9.search.SearchSpecification.SearchCondition;
 
 
 /**
@@ -44,86 +48,36 @@ import com.fsck.k9.search.SearchSpecification;
  */
 public class MessageList extends K9FragmentActivity implements MessageListFragmentListener,
         OnBackStackChangedListener, OnSwipeGestureListener {
-    private static final String EXTRA_ACCOUNT = "account";
-    private static final String EXTRA_FOLDER  = "folder";
+	
+	// for this activity
+    private static final String EXTRA_SEARCH = "search";
+
+    // used for remote search
     private static final String EXTRA_SEARCH_ACCOUNT = "com.fsck.k9.search_account";
     private static final String EXTRA_SEARCH_FOLDER = "com.fsck.k9.search_folder";
-    private static final String EXTRA_QUERY_FLAGS = "queryFlags";
-    private static final String EXTRA_FORBIDDEN_FLAGS = "forbiddenFlags";
-    private static final String EXTRA_INTEGRATE = "integrate";
-    private static final String EXTRA_ACCOUNT_UUIDS = "accountUuids";
-    private static final String EXTRA_FOLDER_NAMES = "folderNames";
-    private static final String EXTRA_TITLE = "title";
 
-
-    public static void actionHandleFolder(Context context, Account account, String folder) {
-        Intent intent = new Intent(context, MessageList.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        intent.putExtra(EXTRA_ACCOUNT, account.getUuid());
-
-        if (folder != null) {
-            intent.putExtra(EXTRA_FOLDER, folder);
-        }
-        context.startActivity(intent);
+    public static void actionDisplaySearch(Context context, SearchSpecification search, boolean newTask) {
+        actionDisplaySearch(context, search, newTask, true);
     }
-
-    public static Intent actionHandleFolderIntent(Context context, Account account, String folder) {
+    
+    public static void actionDisplaySearch(Context context, SearchSpecification search, boolean newTask, boolean clearTop) {
+        context.startActivity(intentDisplaySearch(context, search, newTask, clearTop));
+    }
+    
+    public static Intent intentDisplaySearch(Context context, SearchSpecification search, boolean newTask, boolean clearTop) {
         Intent intent = new Intent(context, MessageList.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP |
-                Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        intent.putExtra(EXTRA_ACCOUNT, account.getUuid());
-
-        if (folder != null) {
-            intent.putExtra(EXTRA_FOLDER, folder);
+        intent.putExtra(EXTRA_SEARCH, search);
+        
+        if (clearTop) {
+        	intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
         }
+        if (newTask) {
+        	intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+        }
+        
         return intent;
     }
 
-    public static void actionHandle(Context context, String title, String queryString, boolean integrate, Flag[] flags, Flag[] forbiddenFlags) {
-        Intent intent = new Intent(context, MessageList.class);
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
-        intent.putExtra(SearchManager.QUERY, queryString);
-        if (flags != null) {
-            intent.putExtra(EXTRA_QUERY_FLAGS, Utility.combine(flags, ','));
-        }
-        if (forbiddenFlags != null) {
-            intent.putExtra(EXTRA_FORBIDDEN_FLAGS, Utility.combine(forbiddenFlags, ','));
-        }
-        intent.putExtra(EXTRA_INTEGRATE, integrate);
-        intent.putExtra(EXTRA_TITLE, title);
-        context.startActivity(intent);
-    }
-
-    /**
-     * Creates and returns an intent that opens Unified Inbox or All Messages screen.
-     */
-    public static Intent actionHandleAccountIntent(Context context, String title,
-            SearchSpecification searchSpecification) {
-        Intent intent = new Intent(context, MessageList.class);
-        intent.putExtra(SearchManager.QUERY, searchSpecification.getQuery());
-        if (searchSpecification.getRequiredFlags() != null) {
-            intent.putExtra(EXTRA_QUERY_FLAGS, Utility.combine(searchSpecification.getRequiredFlags(), ','));
-        }
-        if (searchSpecification.getForbiddenFlags() != null) {
-            intent.putExtra(EXTRA_FORBIDDEN_FLAGS, Utility.combine(searchSpecification.getForbiddenFlags(), ','));
-        }
-        intent.putExtra(EXTRA_INTEGRATE, searchSpecification.isIntegrate());
-        intent.putExtra(EXTRA_ACCOUNT_UUIDS, searchSpecification.getAccountUuids());
-        intent.putExtra(EXTRA_FOLDER_NAMES, searchSpecification.getFolderNames());
-        intent.putExtra(EXTRA_TITLE, title);
-        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
-        intent.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
-
-        return intent;
-    }
-
-    public static void actionHandle(Context context, String title,
-                                    SearchSpecification searchSpecification) {
-        Intent intent = actionHandleAccountIntent(context, title, searchSpecification);
-        context.startActivity(intent);
-    }
-
 
     private StorageManager.StorageListener mStorageListener = new StorageListenerImplementation();
 
@@ -131,31 +85,22 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
     private TextView mActionBarTitle;
     private TextView mActionBarSubTitle;
     private TextView mActionBarUnread;
-    private String mTitle;
     private Menu mMenu;
 
     private MessageListFragment mMessageListFragment;
 
     private Account mAccount;
-    private String mQueryString;
     private String mFolderName;
-    private Flag[] mQueryFlags;
-    private Flag[] mForbiddenFlags;
-    private String mSearchAccount = null;
-    private String mSearchFolder = null;
-    private boolean mIntegrate;
-    private String[] mAccountUuids;
-    private String[] mFolderNames;
-
-
+	private LocalSearch mSearch;
+    private boolean mSingleFolderMode;
+    private boolean mSingleAccountMode;
+    private boolean mIsRemote;
+    
     @Override
     public void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.message_list);
 
-        // need this for actionbar initialization
-        mQueryString = getIntent().getStringExtra(SearchManager.QUERY);
-
         mActionBar = getSupportActionBar();
         initializeActionBar();
 
@@ -171,76 +116,56 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
 
         if (mMessageListFragment == null) {
             FragmentTransaction ft = fragmentManager.beginTransaction();
-            if (mQueryString == null) {
-                mMessageListFragment = MessageListFragment.newInstance(mAccount, mFolderName);
-            } else if (mSearchAccount != null) {
-                mMessageListFragment = MessageListFragment.newInstance(mSearchAccount,
-                        mSearchFolder, mQueryString, false);
-            } else {
-                mMessageListFragment = MessageListFragment.newInstance(mTitle, mAccountUuids,
-                        mFolderNames, mQueryString, mQueryFlags, mForbiddenFlags, mIntegrate);
-            }
+            mMessageListFragment = MessageListFragment.newInstance(mSearch, mIsRemote);
             ft.add(R.id.message_list_container, mMessageListFragment);
             ft.commit();
         }
     }
 
     private void decodeExtras(Intent intent) {
-        mQueryString = intent.getStringExtra(SearchManager.QUERY);
-        mFolderName = null;
-        mSearchAccount = null;
-        mSearchFolder = null;
-        if (mQueryString != null) {
+    	// check if this intent comes from the system search ( remote )
+    	if (intent.getStringExtra(SearchManager.QUERY) != null) {
             if (Intent.ACTION_SEARCH.equals(intent.getAction())) {
                 //Query was received from Search Dialog
                 Bundle appData = getIntent().getBundleExtra(SearchManager.APP_DATA);
                 if (appData != null) {
-                    mSearchAccount = appData.getString(EXTRA_SEARCH_ACCOUNT);
-                    mSearchFolder = appData.getString(EXTRA_SEARCH_FOLDER);
+                    mSearch = new LocalSearch();
+                    mSearch.addAccountUuid(appData.getString(EXTRA_SEARCH_ACCOUNT));
+                    mSearch.addAllowedFolder(appData.getString(EXTRA_SEARCH_FOLDER));
+                    
+                    String query = intent.getStringExtra(SearchManager.QUERY);
+                    mSearch.or(new SearchCondition(SEARCHFIELD.SENDER, ATTRIBUTE.CONTAINS, query));
+                    mSearch.or(new SearchCondition(SEARCHFIELD.SUBJECT, ATTRIBUTE.CONTAINS, query));
+                    
+                    mIsRemote = true;
                 }
-            } else {
-                mSearchAccount = intent.getStringExtra(EXTRA_SEARCH_ACCOUNT);
-                mSearchFolder = intent.getStringExtra(EXTRA_SEARCH_FOLDER);
             }
+        } else {
+        	// regular LocalSearch object was passed
+        	mSearch = intent.getParcelableExtra(EXTRA_SEARCH);      	
         }
 
-        String accountUuid = intent.getStringExtra(EXTRA_ACCOUNT);
-        mFolderName = intent.getStringExtra(EXTRA_FOLDER);
-
-        mAccount = Preferences.getPreferences(this).getAccount(accountUuid);
-
-        if (mAccount != null && !mAccount.isAvailable(this)) {
-            Log.i(K9.LOG_TAG, "not opening MessageList of unavailable account");
-            onAccountUnavailable();
-            return;
-        }
-
-        String queryFlags = intent.getStringExtra(EXTRA_QUERY_FLAGS);
-        if (queryFlags != null) {
-            String[] flagStrings = queryFlags.split(",");
-            mQueryFlags = new Flag[flagStrings.length];
-            for (int i = 0; i < flagStrings.length; i++) {
-                mQueryFlags[i] = Flag.valueOf(flagStrings[i]);
-            }
-        }
-        String forbiddenFlags = intent.getStringExtra(EXTRA_FORBIDDEN_FLAGS);
-        if (forbiddenFlags != null) {
-            String[] flagStrings = forbiddenFlags.split(",");
-            mForbiddenFlags = new Flag[flagStrings.length];
-            for (int i = 0; i < flagStrings.length; i++) {
-                mForbiddenFlags[i] = Flag.valueOf(flagStrings[i]);
-            }
-        }
-        mIntegrate = intent.getBooleanExtra(EXTRA_INTEGRATE, false);
-        mAccountUuids = intent.getStringArrayExtra(EXTRA_ACCOUNT_UUIDS);
-        mFolderNames = intent.getStringArrayExtra(EXTRA_FOLDER_NAMES);
-        mTitle = intent.getStringExtra(EXTRA_TITLE);
-
-        // Take the initial folder into account only if we are *not* restoring
-        // the activity already.
-        if (mFolderName == null && mQueryString == null) {
-            mFolderName = mAccount.getAutoExpandFolderName();
-        }
+	    String[] accounts = mSearch.getAccountUuids();
+	    mSingleAccountMode = ( accounts != null && accounts.length == 1 
+	    		&& !accounts[0].equals(SearchSpecification.ALL_ACCOUNTS));
+	    mSingleFolderMode = mSingleAccountMode && (mSearch.getFolderNames().size() == 1);
+	    
+	    if (mSingleAccountMode) {
+		    mAccount = Preferences.getPreferences(this).getAccount(accounts[0]);
+		    
+		    if (mAccount != null && !mAccount.isAvailable(this)) {
+		        Log.i(K9.LOG_TAG, "not opening MessageList of unavailable account");
+		        onAccountUnavailable();
+		        return;
+		    }
+	    }
+	    
+	    if (mSingleFolderMode) {
+	    	mFolderName = mSearch.getFolderNames().get(0);
+	    }
+	    
+	    // now we know if we are in single account mode and need a subtitle
+	    mActionBarSubTitle.setVisibility((!mSingleFolderMode) ? View.GONE : View.VISIBLE);
     }
 
     @Override
@@ -276,10 +201,6 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
         mActionBarSubTitle = (TextView) customView.findViewById(R.id.actionbar_title_sub);
         mActionBarUnread = (TextView) customView.findViewById(R.id.actionbar_unread_count);
 
-        if (mQueryString != null) {
-            mActionBarSubTitle.setVisibility(View.GONE);
-        }
-
         mActionBar.setDisplayHomeAsUpEnabled(true);
     }
 
@@ -407,17 +328,11 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
                 FragmentManager fragmentManager = getSupportFragmentManager();
                 if (fragmentManager.getBackStackEntryCount() > 0) {
                     fragmentManager.popBackStack();
-                } else if (mIntegrate) {
-                    // If we were in one of the integrated mailboxes (think All Mail or Integrated Inbox), then
-                    // go to accounts.
-                    onAccounts();
-                } else if (mQueryString != null) {
-                    // We did a search of some sort.  Go back to wherever the user searched from.
-                    onBackPressed();
-                } else {
-                    // In a standard message list of a folder.  Go to folder list.
-                    onShowFolderList();
-                }
+                } else if (!mSingleFolderMode) {
+            		onBackPressed();
+            	} else {
+            		onShowFolderList();
+            	}
                 return true;
             }
             case R.id.compose: {
@@ -470,7 +385,7 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
             }
         }
 
-        if (mQueryString != null) {
+        if (!mSingleFolderMode) {
             // None of the options after this point are "safe" for search results
             //TODO: This is not true for "unread" and "starred" searches in regular folders
             return false;
@@ -534,7 +449,7 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
             menu.findItem(R.id.select_all).setVisible(true);
             menu.findItem(R.id.settings).setVisible(true);
 
-            if (mMessageListFragment.isSearchQuery()) {
+            if (!mSingleAccountMode) {
                 menu.findItem(R.id.expunge).setVisible(false);
                 menu.findItem(R.id.check_mail).setVisible(false);
                 menu.findItem(R.id.send_messages).setVisible(false);
@@ -666,8 +581,11 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
 
     @Override
     public void showMoreFromSameSender(String senderAddress) {
-        MessageListFragment fragment = MessageListFragment.newInstance("From " + senderAddress,
-                null, null, senderAddress, null, null, false);
+    	LocalSearch tmpSearch = new LocalSearch("From " + senderAddress);
+    	tmpSearch.addAccountUuids(mSearch.getAccountUuids());
+    	tmpSearch.and(SEARCHFIELD.SENDER, senderAddress, ATTRIBUTE.CONTAINS);
+    	
+        MessageListFragment fragment = MessageListFragment.newInstance(tmpSearch, false);
 
         addMessageListFragment(fragment);
     }
@@ -716,8 +634,7 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
 
     @Override
     public void remoteSearch(String searchAccount, String searchFolder, String queryString) {
-        MessageListFragment fragment = MessageListFragment.newInstance(searchAccount, searchFolder,
-                queryString, true);
+        MessageListFragment fragment = MessageListFragment.newInstance(mSearch, true);
 
         addMessageListFragment(fragment);
     }
@@ -751,9 +668,11 @@ public class MessageList extends K9FragmentActivity implements MessageListFragme
 
     @Override
     public void showThread(Account account, String folderName, long threadRootId) {
-        MessageListFragment fragment = MessageListFragment.newInstance(account, folderName,
-                threadRootId);
-
+    	LocalSearch tmpSearch = new LocalSearch();
+    	tmpSearch.addAccountUuids(mSearch.getAccountUuids());
+    	tmpSearch.and(SEARCHFIELD.THREAD_ROOT, String.valueOf(threadRootId), ATTRIBUTE.EQUALS);
+    	
+        MessageListFragment fragment = MessageListFragment.newInstance(tmpSearch, false);
         addMessageListFragment(fragment);
     }
 }
diff --git a/src/com/fsck/k9/fragment/MessageListFragment.java b/src/com/fsck/k9/fragment/MessageListFragment.java
index 1fab56b86..3932fed90 100644
--- a/src/com/fsck/k9/fragment/MessageListFragment.java
+++ b/src/com/fsck/k9/fragment/MessageListFragment.java
@@ -77,6 +77,12 @@ import com.fsck.k9.mail.Store;
 import com.fsck.k9.mail.store.LocalStore;
 import com.fsck.k9.mail.store.LocalStore.LocalFolder;
 import com.fsck.k9.mail.store.LocalStore.LocalMessage;
+import com.fsck.k9.search.ConditionsTreeNode;
+import com.fsck.k9.search.LocalSearch;
+import com.fsck.k9.search.SearchSpecification;
+import com.fsck.k9.search.SearchSpecification.ATTRIBUTE;
+import com.fsck.k9.search.SearchSpecification.SEARCHFIELD;
+import com.fsck.k9.search.SearchSpecification.SearchCondition;
 import com.handmark.pulltorefresh.library.PullToRefreshBase;
 import com.handmark.pulltorefresh.library.PullToRefreshListView;
 
@@ -84,67 +90,14 @@ import com.handmark.pulltorefresh.library.PullToRefreshListView;
 public class MessageListFragment extends SherlockFragment implements OnItemClickListener,
         ConfirmationDialogFragmentListener {
 
-    public static MessageListFragment newInstance(Account account, String folderName) {
+	public static MessageListFragment newInstance(LocalSearch search, boolean remoteSearch) {
         MessageListFragment fragment = new MessageListFragment();
-
         Bundle args = new Bundle();
-        args.putString(ARG_ACCOUNT, account.getUuid());
-        args.putString(ARG_FOLDER, folderName);
-        fragment.setArguments(args);
-
-        return fragment;
-    }
-
-    public static MessageListFragment newInstance(Account account, String folderName,
-            long threadRootId) {
-        MessageListFragment fragment = new MessageListFragment();
-
-        Bundle args = new Bundle();
-        args.putString(ARG_ACCOUNT, account.getUuid());
-        args.putString(ARG_FOLDER, folderName);
-        args.putLong(ARG_THREAD_ID, threadRootId);
-        fragment.setArguments(args);
-
-        return fragment;
-    }
-
-    public static MessageListFragment newInstance(String title, String[] accountUuids,
-            String[] folderNames, String queryString, Flag[] flags,
-            Flag[] forbiddenFlags, boolean integrate) {
-
-        MessageListFragment fragment = new MessageListFragment();
-
-        Bundle args = new Bundle();
-        args.putStringArray(ARG_ACCOUNT_UUIDS, accountUuids);
-        args.putStringArray(ARG_FOLDER_NAMES, folderNames);
-        args.putString(ARG_QUERY, queryString);
-        if (flags != null) {
-            args.putString(ARG_QUERY_FLAGS, Utility.combine(flags, ','));
-        }
-        if (forbiddenFlags != null) {
-            args.putString(ARG_FORBIDDEN_FLAGS, Utility.combine(forbiddenFlags, ','));
-        }
-        args.putBoolean(ARG_INTEGRATE, integrate);
-        args.putString(ARG_TITLE, title);
-        fragment.setArguments(args);
-
-        return fragment;
-    }
-
-    public static MessageListFragment newInstance(String searchAccount, String searchFolder,
-            String queryString, boolean remoteSearch) {
-        MessageListFragment fragment = new MessageListFragment();
-
-        Bundle args = new Bundle();
-        args.putString(ARG_SEARCH_ACCOUNT, searchAccount);
-        args.putString(ARG_SEARCH_FOLDER, searchFolder);
-        args.putString(ARG_QUERY, queryString);
+        args.putParcelable(ARG_SEARCH, search);
         args.putBoolean(ARG_REMOTE_SEARCH, remoteSearch);
         fragment.setArguments(args);
-
-        return fragment;
-    }
-
+        return fragment;	
+	}
 
     /**
      * Reverses the result of a {@link Comparator}.
@@ -292,20 +245,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
     private static final int ACTIVITY_CHOOSE_FOLDER_MOVE = 1;
     private static final int ACTIVITY_CHOOSE_FOLDER_COPY = 2;
 
-    private static final String ARG_ACCOUNT = "account";
-    private static final String ARG_FOLDER  = "folder";
-    private static final String ARG_REMOTE_SEARCH = "remote_search";
-    private static final String ARG_QUERY = "query";
-    private static final String ARG_SEARCH_ACCOUNT = "search_account";
-    private static final String ARG_SEARCH_FOLDER = "search_folder";
-    private static final String ARG_QUERY_FLAGS = "queryFlags";
-    private static final String ARG_FORBIDDEN_FLAGS = "forbiddenFlags";
-    private static final String ARG_INTEGRATE = "integrate";
-    private static final String ARG_ACCOUNT_UUIDS = "accountUuids";
-    private static final String ARG_FOLDER_NAMES = "folderNames";
-    private static final String ARG_TITLE = "title";
-    private static final String ARG_THREAD_ID = "thread_id";
-
+    private static final String ARG_SEARCH = "searchObject";
+    private static final String ARG_REMOTE_SEARCH = "remoteSearch";
     private static final String STATE_LIST_POSITION = "listPosition";
 
     /**
@@ -356,18 +297,14 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
     /**
      * If we're doing a search, this contains the query string.
      */
-    private String mQueryString;
-    private Flag[] mQueryFlags = null;
-    private Flag[] mForbiddenFlags = null;
     private boolean mRemoteSearch = false;
-    private String mSearchAccount = null;
-    private String mSearchFolder = null;
     private Future mRemoteSearchFuture = null;
-    private boolean mIntegrate = false;
-    private String[] mAccountUuids = null;
-    private String[] mFolderNames = null;
+    
     private String mTitle;
-
+    private LocalSearch mSearch = null;
+    private boolean mSingleAccountMode;
+    private boolean mSingleFolderMode;
+    
     private MessageListHandler mHandler = new MessageListHandler();
 
     private SortType mSortType = SortType.SORT_DATE;
@@ -596,7 +533,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
 
     private void setWindowTitle() {
         // regular folder content display
-        if (mFolderName != null) {
+        if (mSingleFolderMode) {
             Activity activity = getActivity();
             String displayName = FolderInfoHolder.getDisplayName(activity, mAccount,
                 mFolderName);
@@ -609,7 +546,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
             } else {
                 mFragmentListener.setMessageListSubTitle(operation);
             }
-        } else if (mQueryString != null) {
+        } else {
             // query result display.  This may be for a search folder as opposed to a user-initiated search.
             if (mTitle != null) {
                 // This was a search folder; the search folder has overridden our title.
@@ -626,8 +563,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
         if (mUnreadMessageCount == 0) {
             mFragmentListener.setUnreadCount(0);
         } else {
-            if (mQueryString != null && mTitle == null) {
-                // This is a search result.  The unread message count is easily confused
+            if (!mSingleFolderMode && mTitle == null) {
+                // The unread message count is easily confused
                 // with total number of messages in the search result, so let's hide it.
                 mFragmentListener.setUnreadCount(0);
             } else {
@@ -665,15 +602,14 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
         if (view == mFooterView) {
             if (mCurrentFolder != null && !mRemoteSearch) {
                 mController.loadMoreMessages(mAccount, mFolderName, mAdapter.mListener);
-            } else if (mRemoteSearch && mAdapter.mExtraSearchResults != null && mAdapter.mExtraSearchResults.size() > 0 && mSearchAccount != null) {
+            } else if (mRemoteSearch && mAdapter.mExtraSearchResults != null && mAdapter.mExtraSearchResults.size() > 0 && mAccount != null) {
                 int numResults = mAdapter.mExtraSearchResults.size();
                 Context appContext = getActivity().getApplicationContext();
-                Account account = Preferences.getPreferences(appContext).getAccount(mSearchAccount);
-                if (account == null) {
+                if (mAccount == null) {
                     mHandler.updateFooter("", false);
                     return;
                 }
-                int limit = account.getRemoteSearchNumResults();
+                int limit = mAccount.getRemoteSearchNumResults();
                 List toProcess = mAdapter.mExtraSearchResults;
                 if (limit > 0 && numResults > limit) {
                     toProcess = toProcess.subList(0, limit);
@@ -682,7 +618,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
                     mAdapter.mExtraSearchResults = null;
                     mHandler.updateFooter("", false);
                 }
-                mController.loadSearchResults(account, mSearchFolder, toProcess, mAdapter.mListener);
+                mController.loadSearchResults(mAccount, mCurrentFolder.name, toProcess, mAdapter.mListener);
             }
             return;
         }
@@ -754,56 +690,58 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
     private void decodeArguments() {
         Bundle args = getArguments();
 
-        mQueryString = args.getString(SearchManager.QUERY);
-        mFolderName = args.getString(ARG_FOLDER);
         mRemoteSearch = args.getBoolean(ARG_REMOTE_SEARCH, false);
-        mSearchAccount = args.getString(ARG_SEARCH_ACCOUNT);
-        mSearchFolder = args.getString(ARG_SEARCH_FOLDER);
-        mThreadId = args.getLong(ARG_THREAD_ID, -1);
-
-        String accountUuid = args.getString(ARG_ACCOUNT);
-
-        Context appContext = getActivity().getApplicationContext();
-        mAccount = Preferences.getPreferences(appContext).getAccount(accountUuid);
-
-        String queryFlags = args.getString(ARG_QUERY_FLAGS);
-        if (queryFlags != null) {
-            String[] flagStrings = queryFlags.split(",");
-            mQueryFlags = new Flag[flagStrings.length];
-            for (int i = 0; i < flagStrings.length; i++) {
-                mQueryFlags[i] = Flag.valueOf(flagStrings[i]);
-            }
-        }
-
-        String forbiddenFlags = args.getString(ARG_FORBIDDEN_FLAGS);
-        if (forbiddenFlags != null) {
-            String[] flagStrings = forbiddenFlags.split(",");
-            mForbiddenFlags = new Flag[flagStrings.length];
-            for (int i = 0; i < flagStrings.length; i++) {
-                mForbiddenFlags[i] = Flag.valueOf(flagStrings[i]);
-            }
-        }
-
-        mIntegrate = args.getBoolean(ARG_INTEGRATE, false);
-        mAccountUuids = args.getStringArray(ARG_ACCOUNT_UUIDS);
-        mFolderNames = args.getStringArray(ARG_FOLDER_NAMES);
-        mTitle = args.getString(ARG_TITLE);
+        mSearch = args.getParcelable(ARG_SEARCH);
+        mTitle = args.getString(mSearch.getName());
+        
+        Context appContext = getActivity().getApplicationContext();  
+	    String[] accounts = mSearch.getAccountUuids();
+	    
+	    mSingleAccountMode = false;
+	    if (accounts != null && accounts.length == 1 
+	    		&& !accounts[0].equals(SearchSpecification.ALL_ACCOUNTS)) {
+	    	mSingleAccountMode = true;
+		    mAccount = Preferences.getPreferences(appContext).getAccount(accounts[0]);
+	    }
+	    
+	    mSingleFolderMode = false;
+	    if (mSingleAccountMode && (mSearch.getFolderNames().size() == 1)) {
+	    	mSingleFolderMode = true;
+	    	mFolderName = mSearch.getFolderNames().get(0);
+	    	mCurrentFolder = getFolder(mFolderName, mAccount);
+	    }
     }
 
     private void initializeMessageList() {
         mAdapter = new MessageListAdapter();
 
         if (mFolderName != null) {
-            mCurrentFolder = mAdapter.getFolder(mFolderName, mAccount);
+            mCurrentFolder = getFolder(mFolderName, mAccount);
         }
-
-        // Hide "Load up to x more" footer for search views
-        mFooterView.setVisibility((mQueryString != null) ? View.GONE : View.VISIBLE);
+        
+	    // Hide "Load up to x more" footer for search views
+	    mFooterView.setVisibility((!mSingleFolderMode) ? View.GONE : View.VISIBLE);
 
         mController = MessagingController.getInstance(getActivity().getApplication());
         mListView.setAdapter(mAdapter);
     }
 
+    private FolderInfoHolder getFolder(String folder, Account account) {
+        LocalFolder local_folder = null;
+        try {
+            LocalStore localStore = account.getLocalStore();
+            local_folder = localStore.getFolder(folder);
+            return new FolderInfoHolder(mContext, local_folder, account);
+        } catch (Exception e) {
+            Log.e(K9.LOG_TAG, "getFolder(" + folder + ") goes boom: ", e);
+            return null;
+        } finally {
+            if (local_folder != null) {
+                local_folder.close();
+            }
+        }
+    }
+    
     @Override
     public void onPause() {
         super.onPause();
@@ -851,14 +789,6 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
 
         final Preferences prefs = Preferences.getPreferences(appContext);
 
-        boolean allowRemoteSearch = false;
-        if (mSearchAccount != null) {
-            final Account searchAccount = prefs.getAccount(mSearchAccount);
-            if (searchAccount != null) {
-                allowRemoteSearch = searchAccount.allowRemoteSearch();
-            }
-        }
-
         // Check if we have connectivity.  Cache the value.
         if (mHasConnectivity == null) {
             final ConnectivityManager connectivityManager =
@@ -872,24 +802,26 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
             }
         }
 
-        if (mQueryString == null) {
-            mPullToRefreshView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener() {
-                @Override
-                public void onRefresh(PullToRefreshBase refreshView) {
-                    checkMail();
-                }
-            });
-        } else if (allowRemoteSearch && !mRemoteSearch && !mIntegrate && mHasConnectivity) {
-            // mQueryString != null is implied if we get this far.
-            mPullToRefreshView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener() {
-                @Override
-                public void onRefresh(PullToRefreshBase refreshView) {
-                    mPullToRefreshView.onRefreshComplete();
-                    onRemoteSearchRequested(true);
-                }
-            });
-            mPullToRefreshView.setPullLabel(getString(R.string.pull_to_refresh_remote_search_from_local_search_pull));
-            mPullToRefreshView.setReleaseLabel(getString(R.string.pull_to_refresh_remote_search_from_local_search_release));
+        if (mSingleFolderMode) {
+        	if (!mAccount.allowRemoteSearch()) {
+	            mPullToRefreshView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener() {
+	                @Override
+	                public void onRefresh(PullToRefreshBase refreshView) {
+	                    checkMail();
+	                }
+	            });
+	        // TODO this has to go! find better remote search integration
+        	} else {
+                mPullToRefreshView.setOnRefreshListener(new PullToRefreshBase.OnRefreshListener() {
+                    @Override
+                    public void onRefresh(PullToRefreshBase refreshView) {
+                        mPullToRefreshView.onRefreshComplete();
+                        onRemoteSearchRequested();
+                    }
+                });
+                mPullToRefreshView.setPullLabel(getString(R.string.pull_to_refresh_remote_search_from_local_search_pull));
+                mPullToRefreshView.setReleaseLabel(getString(R.string.pull_to_refresh_remote_search_from_local_search_release));
+        	}
         } else {
             mPullToRefreshView.setMode(PullToRefreshBase.Mode.DISABLED);
         }
@@ -899,7 +831,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
         //Cancel pending new mail notifications when we open an account
         Account[] accountsWithNotification;
 
-        Account account = getCurrentAccount(prefs);
+        Account account = mAccount;
 
         if (account != null) {
             accountsWithNotification = new Account[] { account };
@@ -918,9 +850,40 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
         }
 
         if (mAdapter.isEmpty()) {
+        	mController.searchLocalMessages(mSearch, mAdapter.mListener);
             if (mRemoteSearch) {
                 //TODO: Support flag based search
-                mRemoteSearchFuture = mController.searchRemoteMessages(mSearchAccount, mSearchFolder, mQueryString, null, null, mAdapter.mListener);
+               /* mRemoteSearchFuture = mController.searchRemoteMessages(mAccount.getUuid(), mCurrentFolder.name, mSearch.getRemoteSearchArguments(),
+                			null, null, mAdapter.mListener);*/
+            }
+        } else {
+            // reread the selected date format preference in case it has changed
+            mMessageHelper.refresh();
+            mAdapter.markAllMessagesAsDirty();
+  
+            new Thread() {
+                @Override
+                public void run() {
+                	mController.searchLocalMessages(mSearch, mAdapter.mListener);
+
+                    mHandler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            mAdapter.pruneDirtyMessages();
+                            mAdapter.notifyDataSetChanged();
+                            restoreListState();
+                        }
+                    });
+                }
+
+            }.start();
+        }
+        
+        /*if (mAdapter.isEmpty()) {
+            if (mRemoteSearch) {
+                //TODO: Support flag based search
+                mRemoteSearchFuture = mController.searchRemoteMessages(mAccount.getUuid(), mCurrentFolder.name, mSearch.getRemoteSearchArguments(),
+                			null, null, mAdapter.mListener);
             } else if (mFolderName != null) {
                 mController.listLocalMessages(mAccount, mFolderName,  mAdapter.mListener, mThreadViewEnabled, mThreadId);
 
@@ -928,8 +891,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
                 if (!mAccount.hasArchiveFolder()) {
 //                    mBatchArchiveButton.setVisibility(View.GONE);
                 }
-            } else if (mQueryString != null) {
-                mController.searchLocalMessages(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener);
+            } else {
+                mController.searchLocalMessages(mSearch, mAdapter.mListener);
                 // Don't show the archive button if this is a search.
 //                mBatchArchiveButton.setVisibility(View.GONE);
             }
@@ -944,11 +907,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
                 new Thread() {
                     @Override
                     public void run() {
-                        if (mFolderName != null) {
-                            mController.listLocalMessagesSynchronous(mAccount, mFolderName,  mAdapter.mListener, mThreadViewEnabled, mThreadId);
-                        } else if (mQueryString != null) {
-                            mController.searchLocalMessagesSynchronous(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener);
-                        }
+                    	mController.searchLocalMessages(mSearch, mAdapter.mListener);
 
                         mHandler.post(new Runnable() {
                             @Override
@@ -963,7 +922,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
                 }
                 .start();
             }
-        }
+        }*/
 
         if (mAccount != null && mFolderName != null && !mRemoteSearch) {
             mController.getFolderUnreadMessageCount(mAccount, mFolderName, mAdapter.mListener);
@@ -989,7 +948,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
     }
 
     public void onCompose() {
-        if (mQueryString != null) {
+        if (!mSingleAccountMode) {
             /*
              * If we have a query string, we don't have an account to let
              * compose start the default action.
@@ -1023,22 +982,15 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
 
     /**
      * User has requested a remote search.  Setup the bundle and start the intent.
-     * @param fromLocalSearch true if this is being called from a local search result screen.  This affects
-     *                        where we pull the account and folder info used for the next search.
      */
-    public void onRemoteSearchRequested(final boolean fromLocalSearch) {
+    public void onRemoteSearchRequested() {
         String searchAccount;
         String searchFolder;
 
-        if (fromLocalSearch) {
-            searchAccount = mSearchAccount;
-            searchFolder = mSearchFolder;
-        } else {
-            searchAccount = mAccount.getUuid();
-            searchFolder = mCurrentFolder.name;
-        }
+        searchAccount = mAccount.getUuid();
+        searchFolder = mCurrentFolder.name;
 
-        mFragmentListener.remoteSearch(searchAccount, searchFolder, mQueryString);
+        mFragmentListener.remoteSearch(searchAccount, searchFolder, mSearch.getRemoteSearchArguments());
     }
 
     /**
@@ -1055,7 +1007,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
         mSortType = sortType;
 
         Preferences prefs = Preferences.getPreferences(getActivity().getApplicationContext());
-        Account account = getCurrentAccount(prefs);
+        Account account = mAccount;
 
         if (account != null) {
             account.setSortType(mSortType);
@@ -1247,7 +1199,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
         }
         }
 
-        if (mQueryString != null) {
+        if (!mSingleAccountMode) {
             // None of the options after this point are "safe" for search results
             //TODO: This is not true for "unread" and "starred" searches in regular folders
             return false;
@@ -1531,7 +1483,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
 
             @Override
             public void listLocalMessagesStarted(Account account, String folder) {
-                if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
+                if ((!mSingleAccountMode && folder == null) || (account != null && account.equals(mAccount))) {
                     mHandler.progress(true);
                     if (folder != null) {
                         mHandler.folderLoading(folder, true);
@@ -1541,7 +1493,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
 
             @Override
             public void listLocalMessagesFailed(Account account, String folder, String message) {
-                if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
+                if ((!mSingleAccountMode && folder == null) || (account != null && account.equals(mAccount))) {
                     mHandler.sortMessages();
                     mHandler.progress(false);
                     if (folder != null) {
@@ -1552,7 +1504,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
 
             @Override
             public void listLocalMessagesFinished(Account account, String folder) {
-                if ((mQueryString != null && folder == null) || (account != null && account.equals(mAccount))) {
+                if ((!mSingleAccountMode && folder == null) || (account != null && account.equals(mAccount))) {
                     mHandler.sortMessages();
                     mHandler.progress(false);
                     if (folder != null) {
@@ -1601,12 +1553,17 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
             }
         };
 
-        private boolean updateForMe(Account account, String folder) {
+        private boolean updateForMe(Account account, String folder) {     	
+        	// TODO get a contentprovider :D
+        	return true;
+        	
+        	/*
             if ((account.equals(mAccount) && mFolderName != null && folder.equals(mFolderName))) {
                 return true;
             } else {
                 return false;
             }
+            */
         }
 
         public List getMessages() {
@@ -1726,7 +1683,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
         }
 
         public void resetUnreadCount() {
-            if (mQueryString != null) {
+            if (!mSingleFolderMode) {
                 int unreadCount = 0;
 
                 for (MessageInfoHolder holder : mMessages) {
@@ -1802,7 +1759,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
                             } else {
                                 messagesToAdd.add(m);
                             }
-                        } else {
+                        } /*else {
                             if (mQueryString != null) {
                                 if (verifyAgainstSearch) {
                                     messagesToSearch.add(message);
@@ -1815,7 +1772,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
                                     messagesToAdd.add(m);
                                 }
                             }
-                        }
+                        }*/
                     } else {
                         m.dirty = false; // as we reload the message, unset its dirty flag
                         FolderInfoHolder folderInfoHolder = new FolderInfoHolder(mContext,
@@ -1827,9 +1784,29 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick
             }
 
             if (!messagesToSearch.isEmpty()) {
-                mController.searchLocalMessages(mAccountUuids, mFolderNames,
-                        messagesToSearch.toArray(EMPTY_MESSAGE_ARRAY), mQueryString, mIntegrate,
-                        mQueryFlags, mForbiddenFlags,
+            	// building a tree with all possible message id's we want to search in
+            	ConditionsTreeNode msgIdTree = new ConditionsTreeNode(
+            			new SearchCondition(SEARCHFIELD.UID, ATTRIBUTE.EQUALS, 
+            					String.valueOf(messagesToSearch.get(0).getUid())));
+            	
+            	if (messagesToSearch.size() > 1) {
+            		for(int i=1; i