mirror of
https://github.com/moparisthebest/k-9
synced 2025-01-12 06:08:25 -05: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:
parent
73757af680
commit
251428e963
@ -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()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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);
|
||||||
|
@ -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,
|
||||||
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -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
|
||||||
|
@ -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(")");
|
||||||
|
@ -80,7 +80,8 @@ public interface SearchSpecification extends Parcelable {
|
|||||||
ID,
|
ID,
|
||||||
INTEGRATE,
|
INTEGRATE,
|
||||||
READ,
|
READ,
|
||||||
FLAGGED
|
FLAGGED,
|
||||||
|
DISPLAY_CLASS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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: {
|
||||||
|
Loading…
Reference in New Issue
Block a user