1
0
mirror of https://github.com/moparisthebest/k-9 synced 2024-08-13 17:03:48 -04:00

Restore behavior of unread/flagged filtered message list

The unread/flagged count/view for accounts now excludes special folders
and only includes displayable folders as specified by the display class.
This commit is contained in:
cketti 2012-12-07 12:03:04 +01:00
parent 73757af680
commit 251428e963
8 changed files with 192 additions and 18 deletions

View File

@ -27,11 +27,18 @@ import com.fsck.k9.helper.Utility;
import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Address;
import com.fsck.k9.mail.MessagingException; import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Store; import com.fsck.k9.mail.Store;
import com.fsck.k9.mail.Folder.FolderClass;
import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.mail.store.StorageManager; import com.fsck.k9.mail.store.StorageManager;
import com.fsck.k9.mail.store.StorageManager.StorageProvider; import com.fsck.k9.mail.store.StorageManager.StorageProvider;
import com.fsck.k9.provider.EmailProvider; import com.fsck.k9.provider.EmailProvider;
import com.fsck.k9.provider.EmailProvider.StatsColumns; import com.fsck.k9.provider.EmailProvider.StatsColumns;
import com.fsck.k9.search.ConditionsTreeNode;
import com.fsck.k9.search.LocalSearch;
import com.fsck.k9.search.SqlQueryBuilder;
import com.fsck.k9.search.SearchSpecification.Attribute;
import com.fsck.k9.search.SearchSpecification.SearchCondition;
import com.fsck.k9.search.SearchSpecification.Searchfield;
import com.fsck.k9.view.ColorChip; import com.fsck.k9.view.ColorChip;
import java.util.HashMap; import java.util.HashMap;
@ -783,10 +790,22 @@ public class Account implements BaseAccount {
StatsColumns.FLAGGED_COUNT StatsColumns.FLAGGED_COUNT
}; };
//TODO: Only count messages in folders that are displayed, exclude special folders like // Create LocalSearch instance to exclude special folders (Trash, Drafts, Spam, Outbox,
// Trash, Spam, Outbox, Drafts, Sent. // Sent) and limit the search to displayable folders.
LocalSearch search = new LocalSearch();
excludeSpecialFolders(search);
limitToDisplayableFolders(search);
Cursor cursor = cr.query(uri, projection, null, null, null); // Use the LocalSearch instance to create a WHERE clause to query the content provider
StringBuilder query = new StringBuilder();
List<String> queryArgs = new ArrayList<String>();
ConditionsTreeNode conditions = search.getConditions();
SqlQueryBuilder.buildWhereClause(this, conditions, query, queryArgs);
String selection = query.toString();
String[] selectionArgs = queryArgs.toArray(new String[0]);
Cursor cursor = cr.query(uri, projection, selection, selectionArgs, null);
try { try {
if (cursor.moveToFirst()) { if (cursor.moveToFirst()) {
stats.unreadMessageCount = cursor.getInt(0); stats.unreadMessageCount = cursor.getInt(0);
@ -1763,5 +1782,83 @@ public class Account implements BaseAccount {
mRemoteSearchFullText = val; mRemoteSearchFullText = val;
} }
/**
* Modify the supplied {@link LocalSearch} instance to limit the search to displayable folders.
*
* <p>
* This method uses the current folder display mode to decide what folders to include/exclude.
* </p>
*
* @param search
* The {@code LocalSearch} instance to modify.
*
* @see #getFolderDisplayMode()
*/
public void limitToDisplayableFolders(LocalSearch search) {
final Account.FolderMode displayMode = getFolderDisplayMode();
switch (displayMode) {
case FIRST_CLASS: {
// Count messages in the INBOX and non-special first class folders
search.and(Searchfield.DISPLAY_CLASS, FolderClass.FIRST_CLASS.name(),
Attribute.EQUALS);
break;
}
case FIRST_AND_SECOND_CLASS: {
// Count messages in the INBOX and non-special first and second class folders
search.and(Searchfield.DISPLAY_CLASS, FolderClass.FIRST_CLASS.name(),
Attribute.EQUALS);
// TODO: Create a proper interface for creating arbitrary condition trees
SearchCondition searchCondition = new SearchCondition(Searchfield.DISPLAY_CLASS,
Attribute.EQUALS, FolderClass.SECOND_CLASS.name());
ConditionsTreeNode root = search.getConditions();
if (root.mRight != null) {
root.mRight.or(searchCondition);
} else {
search.or(searchCondition);
}
break;
}
case NOT_SECOND_CLASS: {
// Count messages in the INBOX and non-special non-second-class folders
search.and(Searchfield.DISPLAY_CLASS, FolderClass.SECOND_CLASS.name(),
Attribute.NOT_EQUALS);
break;
}
default:
case ALL: {
// Count messages in the INBOX and non-special folders
break;
}
}
}
/**
* Modify the supplied {@link LocalSearch} instance to exclude special folders.
*
* <p>
* Currently the following folders are excluded:
* <ul>
* <li>Trash</li>
* <li>Drafts</li>
* <li>Spam</li>
* <li>Outbox</li>
* <li>Sent</li>
* </ul>
* The Inbox will always be included even if one of the special folders is configured to point
* to the Inbox.
* </p>
*
* @param search
* The {@code LocalSearch} instance to modify.
*/
public void excludeSpecialFolders(LocalSearch search) {
search.and(Searchfield.FOLDER, getTrashFolderName(), Attribute.NOT_EQUALS);
search.and(Searchfield.FOLDER, getDraftsFolderName(), Attribute.NOT_EQUALS);
search.and(Searchfield.FOLDER, getSpamFolderName(), Attribute.NOT_EQUALS);
search.and(Searchfield.FOLDER, getOutboxFolderName(), Attribute.NOT_EQUALS);
search.and(Searchfield.FOLDER, getSentFolderName(), Attribute.NOT_EQUALS);
search.or(new SearchCondition(Searchfield.FOLDER, Attribute.EQUALS, getInboxFolderName()));
}
} }

View File

@ -68,7 +68,6 @@ import com.fsck.k9.activity.setup.AccountSetupBasics;
import com.fsck.k9.activity.setup.Prefs; import com.fsck.k9.activity.setup.Prefs;
import com.fsck.k9.activity.setup.WelcomeMessage; import com.fsck.k9.activity.setup.WelcomeMessage;
import com.fsck.k9.controller.MessagingController; import com.fsck.k9.controller.MessagingController;
import com.fsck.k9.controller.MessagingListener;
import com.fsck.k9.helper.SizeFormatter; import com.fsck.k9.helper.SizeFormatter;
import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Flag;
import com.fsck.k9.mail.ServerSettings; import com.fsck.k9.mail.ServerSettings;
@ -1759,6 +1758,10 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
} else { } else {
search = new LocalSearch(searchTitle); search = new LocalSearch(searchTitle);
search.addAccountUuid(account.getUuid()); search.addAccountUuid(account.getUuid());
Account realAccount = (Account) account;
realAccount.excludeSpecialFolders(search);
realAccount.limitToDisplayableFolders(search);
} }
search.and(Searchfield.FLAGGED, "1", Attribute.EQUALS); search.and(Searchfield.FLAGGED, "1", Attribute.EQUALS);
@ -1777,6 +1780,10 @@ public class Accounts extends K9ListActivity implements OnItemClickListener {
} else { } else {
search = new LocalSearch(searchTitle); search = new LocalSearch(searchTitle);
search.addAccountUuid(account.getUuid()); search.addAccountUuid(account.getUuid());
Account realAccount = (Account) account;
realAccount.excludeSpecialFolders(search);
realAccount.limitToDisplayableFolders(search);
} }
search.and(Searchfield.READ, "1", Attribute.NOT_EQUALS); search.and(Searchfield.READ, "1", Attribute.NOT_EQUALS);

View File

@ -3452,9 +3452,7 @@ public class MessagingController implements Runnable {
ConditionsTreeNode conditions = search.getConditions(); ConditionsTreeNode conditions = search.getConditions();
SqlQueryBuilder.buildWhereClause(account, conditions, query, queryArgs); SqlQueryBuilder.buildWhereClause(account, conditions, query, queryArgs);
// Make sure we don't use the empty string as selection String selection = query.toString();
String selection = (query.length() == 0) ? null : query.toString();
String[] selectionArgs = queryArgs.toArray(EMPTY_STRING_ARRAY); String[] selectionArgs = queryArgs.toArray(EMPTY_STRING_ARRAY);
Uri uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, Uri uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI,

View File

@ -6,4 +6,17 @@ public final class StringUtils {
return string == null || string.length() == 0; return string == null || string.length() == 0;
} }
public static boolean containsAny(String haystack, String[] needles) {
if (haystack == null) {
return false;
}
for (String needle : needles) {
if (haystack.contains(needle)) {
return true;
}
}
return false;
}
} }

View File

@ -75,6 +75,15 @@ public class Utility {
return false; return false;
} }
public static boolean arrayContainsAny(Object[] a, Object... o) {
for (Object element : a) {
if (arrayContains(o, element)) {
return true;
}
}
return false;
}
/** /**
* Combines the given array of Objects into a single String using * Combines the given array of Objects into a single String using
* each Object's toString() method and the separator character * each Object's toString() method and the separator character

View File

@ -91,6 +91,27 @@ public class EmailProvider extends ContentProvider {
InternalMessageColumns.MIME_TYPE InternalMessageColumns.MIME_TYPE
}; };
private static final String[] FIXUP_MESSAGES_COLUMNS = {
MessageColumns.ID
};
private static final String[] FOLDERS_COLUMNS = {
FolderColumns.ID,
FolderColumns.NAME,
FolderColumns.LAST_UPDATED,
FolderColumns.UNREAD_COUNT,
FolderColumns.VISIBLE_LIMIT,
FolderColumns.STATUS,
FolderColumns.PUSH_STATE,
FolderColumns.LAST_PUSHED,
FolderColumns.FLAGGED_COUNT,
FolderColumns.INTEGRATE,
FolderColumns.TOP_GROUP,
FolderColumns.POLL_CLASS,
FolderColumns.PUSH_CLASS,
FolderColumns.DISPLAY_CLASS
};
static { static {
UriMatcher matcher = sUriMatcher; UriMatcher matcher = sUriMatcher;
@ -141,6 +162,23 @@ public class EmailProvider extends ContentProvider {
public static final String MIME_TYPE = "mime_type"; public static final String MIME_TYPE = "mime_type";
} }
public interface FolderColumns {
public static final String ID = "id";
public static final String NAME = "name";
public static final String LAST_UPDATED = "last_updated";
public static final String UNREAD_COUNT = "unread_count";
public static final String VISIBLE_LIMIT = "visible_limit";
public static final String STATUS = "status";
public static final String PUSH_STATE = "push_state";
public static final String LAST_PUSHED = "last_pushed";
public static final String FLAGGED_COUNT = "flagged_count";
public static final String INTEGRATE = "integrate";
public static final String TOP_GROUP = "top_group";
public static final String POLL_CLASS = "poll_class";
public static final String PUSH_CLASS = "push_class";
public static final String DISPLAY_CLASS = "display_class";
}
public interface StatsColumns { public interface StatsColumns {
public static final String UNREAD_COUNT = "unread_count"; public static final String UNREAD_COUNT = "unread_count";
public static final String FLAGGED_COUNT = "flagged_count"; public static final String FLAGGED_COUNT = "flagged_count";
@ -151,6 +189,7 @@ public class EmailProvider extends ContentProvider {
StatsColumns.FLAGGED_COUNT StatsColumns.FLAGGED_COUNT
}; };
private Preferences mPreferences; private Preferences mPreferences;
@ -267,8 +306,7 @@ public class EmailProvider extends ContentProvider {
} }
final Cursor cursor; final Cursor cursor;
//TODO: check projection and selection for folder columns if (Utility.arrayContainsAny(projection, (Object[]) FOLDERS_COLUMNS)) {
if (Utility.arrayContains(projection, SpecialColumns.FOLDER_NAME)) {
StringBuilder query = new StringBuilder(); StringBuilder query = new StringBuilder();
query.append("SELECT "); query.append("SELECT ");
boolean first = true; boolean first = true;
@ -292,10 +330,10 @@ public class EmailProvider extends ContentProvider {
query.append(" FROM messages m " + query.append(" FROM messages m " +
"LEFT JOIN folders f ON (m.folder_id = f.id) " + "LEFT JOIN folders f ON (m.folder_id = f.id) " +
"WHERE "); "WHERE ");
query.append(SqlQueryBuilder.addPrefixToSelection(MESSAGES_COLUMNS, query.append(SqlQueryBuilder.addPrefixToSelection(FIXUP_MESSAGES_COLUMNS,
"m.", where)); "m.", where));
query.append(" ORDER BY "); query.append(" ORDER BY ");
query.append(SqlQueryBuilder.addPrefixToSelection(MESSAGES_COLUMNS, query.append(SqlQueryBuilder.addPrefixToSelection(FIXUP_MESSAGES_COLUMNS,
"m.", sortOrder)); "m.", sortOrder));
cursor = db.rawQuery(query.toString(), selectionArgs); cursor = db.rawQuery(query.toString(), selectionArgs);
@ -356,8 +394,7 @@ public class EmailProvider extends ContentProvider {
" FROM messages h JOIN messages m " + " FROM messages h JOIN messages m " +
"ON (h.id = m.thread_root OR h.id = m.id) "); "ON (h.id = m.thread_root OR h.id = m.id) ");
//TODO: check projection and selection for folder columns if (Utility.arrayContainsAny(projection, (Object[]) FOLDERS_COLUMNS)) {
if (Utility.arrayContains(projection, SpecialColumns.FOLDER_NAME)) {
query.append("LEFT JOIN folders f ON (m.folder_id = f.id) "); query.append("LEFT JOIN folders f ON (m.folder_id = f.id) ");
} }
@ -424,13 +461,14 @@ public class EmailProvider extends ContentProvider {
// Table selection // Table selection
sql.append(" FROM messages"); sql.append(" FROM messages");
if (selection != null && selection.contains(SpecialColumns.INTEGRATE)) {
if (StringUtils.containsAny(selection, FOLDERS_COLUMNS)) {
sql.append(" JOIN folders ON (folders.id = messages.folder_id)"); sql.append(" JOIN folders ON (folders.id = messages.folder_id)");
} }
// WHERE clause // WHERE clause
sql.append(" WHERE (deleted=0 AND (empty IS NULL OR empty!=1))"); sql.append(" WHERE (deleted=0 AND (empty IS NULL OR empty!=1))");
if (selection != null) { if (!StringUtils.isNullOrEmpty(selection)) {
sql.append(" AND ("); sql.append(" AND (");
sql.append(selection); sql.append(selection);
sql.append(")"); sql.append(")");

View File

@ -80,7 +80,8 @@ public interface SearchSpecification extends Parcelable {
ID, ID,
INTEGRATE, INTEGRATE,
READ, READ,
FLAGGED FLAGGED,
DISPLAY_CLASS
} }

View File

@ -7,6 +7,7 @@ import com.fsck.k9.mail.MessagingException;
import com.fsck.k9.mail.Folder.OpenMode; import com.fsck.k9.mail.Folder.OpenMode;
import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore;
import com.fsck.k9.mail.store.LocalStore.LocalFolder; import com.fsck.k9.mail.store.LocalStore.LocalFolder;
import com.fsck.k9.search.SearchSpecification.Attribute;
import com.fsck.k9.search.SearchSpecification.SearchCondition; import com.fsck.k9.search.SearchSpecification.SearchCondition;
import com.fsck.k9.search.SearchSpecification.Searchfield; import com.fsck.k9.search.SearchSpecification.Searchfield;
@ -29,7 +30,11 @@ public class SqlQueryBuilder {
case FOLDER: { case FOLDER: {
String folderName = condition.value; String folderName = condition.value;
long folderId = getFolderId(account, folderName); long folderId = getFolderId(account, folderName);
query.append("folder_id = ?"); if (condition.attribute == Attribute.EQUALS) {
query.append("folder_id = ?");
} else {
query.append("folder_id != ?");
}
selectionArgs.add(Long.toString(folderId)); selectionArgs.add(Long.toString(folderId));
break; break;
} }
@ -144,6 +149,10 @@ public class SqlQueryBuilder {
columnName = "flagged"; columnName = "flagged";
break; break;
} }
case DISPLAY_CLASS: {
columnName = "display_class";
break;
}
} }
if (columnName == null) { if (columnName == null) {
@ -220,7 +229,9 @@ public class SqlQueryBuilder {
case FOLDER: case FOLDER:
case ID: case ID:
case INTEGRATE: case INTEGRATE:
case THREAD_ROOT: { case THREAD_ROOT:
case READ:
case FLAGGED: {
return true; return true;
} }
default: { default: {