From 50fcff76fdaee090ae9ac5abc10649c7075473b3 Mon Sep 17 00:00:00 2001 From: cketti Date: Tue, 19 Feb 2013 04:45:14 +0100 Subject: [PATCH] Add a caching layer to EmailProvider Database updates can be surprisingly slow. This lead to slow updates of the user interface which in turn made working with K-9 Mail not as fun as it should be. This commit hopefully changes that. --- src/com/fsck/k9/cache/EmailProviderCache.java | 179 ++++++++++++++++++ .../k9/cache/EmailProviderCacheCursor.java | 129 +++++++++++++ .../k9/controller/MessagingController.java | 139 +++++++------- .../fsck/k9/fragment/MessageListFragment.java | 36 +++- src/com/fsck/k9/mail/store/LocalStore.java | 69 +++---- src/com/fsck/k9/provider/EmailProvider.java | 2 + 6 files changed, 440 insertions(+), 114 deletions(-) create mode 100644 src/com/fsck/k9/cache/EmailProviderCache.java create mode 100644 src/com/fsck/k9/cache/EmailProviderCacheCursor.java diff --git a/src/com/fsck/k9/cache/EmailProviderCache.java b/src/com/fsck/k9/cache/EmailProviderCache.java new file mode 100644 index 000000000..6c923f112 --- /dev/null +++ b/src/com/fsck/k9/cache/EmailProviderCache.java @@ -0,0 +1,179 @@ +package com.fsck.k9.cache; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import android.content.Context; +import android.content.Intent; +import android.net.Uri; +import android.support.v4.content.LocalBroadcastManager; + +import com.fsck.k9.fragment.MessageListFragment; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.store.LocalStore.LocalFolder; +import com.fsck.k9.mail.store.LocalStore.LocalMessage; +import com.fsck.k9.provider.EmailProvider; + +/** + * Cache to bridge the time needed to write (user-initiated) changes to the database. + */ +public class EmailProviderCache { + public static final String ACTION_CACHE_UPDATED = "EmailProviderCache.ACTION_CACHE_UPDATED"; + + private static Context sContext; + private static Map sInstances = + new HashMap(); + + public static synchronized EmailProviderCache getCache(String accountUuid, Context context) { + + if (sContext == null) { + sContext = context.getApplicationContext(); + } + + EmailProviderCache instance = sInstances.get(accountUuid); + if (instance == null) { + instance = new EmailProviderCache(accountUuid); + sInstances.put(accountUuid, instance); + } + + return instance; + } + + + private String mAccountUuid; + private Map> mMessageCache = new HashMap>(); + private Map> mThreadCache = new HashMap>(); + private Map mHiddenMessageCache = new HashMap(); + + + private EmailProviderCache(String accountUuid) { + mAccountUuid = accountUuid; + } + + public String getValueForMessage(Long messageId, String columnName) { + synchronized (mMessageCache) { + Map map = mMessageCache.get(messageId); + return (map == null) ? null : map.get(columnName); + } + } + + public String getValueForThread(Long threadRootId, String columnName) { + synchronized (mThreadCache) { + Map map = mThreadCache.get(threadRootId); + return (map == null) ? null : map.get(columnName); + } + } + + public void setValueForMessages(List messageIds, String columnName, String value) { + synchronized (mMessageCache) { + for (Long messageId : messageIds) { + Map map = mMessageCache.get(messageId); + if (map == null) { + map = new HashMap(); + mMessageCache.put(messageId, map); + } + map.put(columnName, value); + } + } + + notifyChange(); + } + + public void setValueForThreads(List threadRootIds, String columnName, String value) { + synchronized (mThreadCache) { + for (Long threadRootId : threadRootIds) { + Map map = mThreadCache.get(threadRootId); + if (map == null) { + map = new HashMap(); + mThreadCache.put(threadRootId, map); + } + map.put(columnName, value); + } + } + + notifyChange(); + } + + public void removeValueForMessages(List messageIds, String columnName) { + synchronized (mMessageCache) { + for (Long messageId : messageIds) { + Map map = mMessageCache.get(messageId); + if (map != null) { + map.remove(columnName); + if (map.size() == 0) { + mMessageCache.remove(messageId); + } + } + } + } + } + + public void removeValueForThreads(List threadRootIds, String columnName) { + synchronized (mThreadCache) { + for (Long threadRootId : threadRootIds) { + Map map = mThreadCache.get(threadRootId); + if (map != null) { + map.remove(columnName); + if (map.size() == 0) { + mThreadCache.remove(threadRootId); + } + } + } + } + } + + public void hideMessages(List messages) { + synchronized (mHiddenMessageCache) { + for (Message message : messages) { + LocalMessage localMessage = (LocalMessage) message; + long messageId = localMessage.getId(); + long folderId = ((LocalFolder) localMessage.getFolder()).getId(); + mHiddenMessageCache.put(messageId, folderId); + } + } + + notifyChange(); + } + + public boolean isMessageHidden(Long messageId, long folderId) { + synchronized (mHiddenMessageCache) { + Long hiddenInFolder = mHiddenMessageCache.get(messageId); + return (hiddenInFolder != null && hiddenInFolder.longValue() == folderId); + } + } + + public void unhideMessages(Message[] messages) { + synchronized (mHiddenMessageCache) { + for (Message message : messages) { + LocalMessage localMessage = (LocalMessage) message; + long messageId = localMessage.getId(); + long folderId = ((LocalFolder) localMessage.getFolder()).getId(); + Long hiddenInFolder = mHiddenMessageCache.get(messageId); + + if (hiddenInFolder != null && hiddenInFolder.longValue() == folderId) { + mHiddenMessageCache.remove(messageId); + } + } + } + } + + /** + * Notify all concerned parties that the message list has changed. + * + *

Note: + * Notifying the content resolver of the change will cause the {@code CursorLoader} in + * {@link MessageListFragment} to reload the cursor. But especially with flag changes this will + * block because of the DB write operation to update the flags. So additionally we use + * {@link LocalBroadcastManager} to send a {@link #ACTION_CACHE_UPDATED} broadcast. This way + * {@code MessageListFragment} can update the view without reloading the cursor. + *

+ */ + private void notifyChange() { + LocalBroadcastManager.getInstance(sContext).sendBroadcast(new Intent(ACTION_CACHE_UPDATED)); + + Uri uri = Uri.withAppendedPath(EmailProvider.CONTENT_URI, "account/" + mAccountUuid + + "/messages"); + sContext.getContentResolver().notifyChange(uri, null); + } +} diff --git a/src/com/fsck/k9/cache/EmailProviderCacheCursor.java b/src/com/fsck/k9/cache/EmailProviderCacheCursor.java new file mode 100644 index 000000000..f1ef56fd0 --- /dev/null +++ b/src/com/fsck/k9/cache/EmailProviderCacheCursor.java @@ -0,0 +1,129 @@ +package com.fsck.k9.cache; + +import java.util.ArrayList; +import java.util.List; + +import com.fsck.k9.provider.EmailProvider.MessageColumns; +import com.fsck.k9.provider.EmailProvider.ThreadColumns; + +import android.content.Context; +import android.database.Cursor; +import android.database.CursorWrapper; + +/** + * A {@link CursorWrapper} that utilizes {@link EmailProviderCache}. + */ +public class EmailProviderCacheCursor extends CursorWrapper { + private EmailProviderCache mCache; + private List mHiddenRows = new ArrayList(); + private int mMessageIdColumn; + private int mFolderIdColumn; + private int mThreadRootColumn; + private int mPosition; + + + public EmailProviderCacheCursor(String accountUuid, Cursor cursor, Context context) { + super(cursor); + + mCache = EmailProviderCache.getCache(accountUuid, context); + + mMessageIdColumn = cursor.getColumnIndex(MessageColumns.ID); + mFolderIdColumn = cursor.getColumnIndex(MessageColumns.FOLDER_ID); + mThreadRootColumn = cursor.getColumnIndex(ThreadColumns.ROOT); + + if (mMessageIdColumn == -1 || mFolderIdColumn == -1 || mThreadRootColumn == -1) { + throw new IllegalArgumentException("The supplied cursor needs to contain the " + + "following columns: " + MessageColumns.ID + ", " + MessageColumns.FOLDER_ID + + ", " + ThreadColumns.ROOT); + } + + while (cursor.moveToNext()) { + long messageId = cursor.getLong(mMessageIdColumn); + long folderId = cursor.getLong(mFolderIdColumn); + if (mCache.isMessageHidden(messageId, folderId)) { + mHiddenRows.add(cursor.getPosition()); + } + } + + // Reset the cursor position + cursor.moveToFirst(); + cursor.moveToPrevious(); + } + + @Override + public int getInt(int columnIndex) { + long messageId = getLong(mMessageIdColumn); + long threadRootId = getLong(mThreadRootColumn); + + String columnName = getColumnName(columnIndex); + String value = mCache.getValueForMessage(messageId, columnName); + + if (value != null) { + return Integer.valueOf(value); + } + + value = mCache.getValueForThread(threadRootId, columnName); + if (value != null) { + return Integer.valueOf(value); + } + + return super.getInt(columnIndex); + } + + @Override + public int getCount() { + return super.getCount() - mHiddenRows.size(); + } + + @Override + public boolean moveToFirst() { + return moveToPosition(0); + } + + @Override + public boolean moveToLast() { + return moveToPosition(getCount()); + } + + @Override + public boolean moveToNext() { + return moveToPosition(getPosition() + 1); + } + + @Override + public boolean moveToPrevious() { + return moveToPosition(getPosition() - 1); + } + + @Override + public boolean move(int offset) { + return moveToPosition(getPosition() + offset); + } + + @Override + public boolean moveToPosition(int position) { + if (mHiddenRows.size() == 0) { + return super.moveToPosition(position); + } + + mPosition = position; + int newPosition = position; + for (int hiddenRow : mHiddenRows) { + if (hiddenRow > newPosition) { + break; + } + newPosition++; + } + + return super.moveToPosition(newPosition); + } + + @Override + public int getPosition() { + if (mHiddenRows.size() == 0) { + return super.getPosition(); + } + + return mPosition; + } +} diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 3330994c5..6b533100c 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -53,6 +53,7 @@ import com.fsck.k9.activity.MessageReference; import com.fsck.k9.activity.NotificationDeleteConfirmation; import com.fsck.k9.activity.setup.AccountSetupIncoming; import com.fsck.k9.activity.setup.AccountSetupOutgoing; +import com.fsck.k9.cache.EmailProviderCache; import com.fsck.k9.helper.Contacts; import com.fsck.k9.helper.power.TracingPowerManager; import com.fsck.k9.helper.power.TracingPowerManager.TracingWakeLock; @@ -199,9 +200,6 @@ public class MessagingController implements Runnable { */ private Application mApplication; - // Key is accountUuid:folderName:messageUid , value is unimportant - private ConcurrentHashMap deletedUids = new ConcurrentHashMap(); - /** * A holder class for pending notification data * @@ -320,45 +318,69 @@ public class MessagingController implements Runnable { private static final Flag[] SYNC_FLAGS = new Flag[] { Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED, Flag.FORWARDED }; - private String createMessageKey(Account account, String folder, Message message) { - return createMessageKey(account, folder, message.getUid()); + + private void suppressMessages(Account account, List messages) { + EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(), + mApplication.getApplicationContext()); + cache.hideMessages(messages); } - private String createMessageKey(Account account, String folder, String uid) { - return account.getUuid() + ":" + folder + ":" + uid; + private void unsuppressMessages(Account account, Message[] messages) { + EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(), + mApplication.getApplicationContext()); + cache.unhideMessages(messages); } - private void suppressMessage(Account account, String folder, Message message) { + private boolean isMessageSuppressed(Account account, Message message) { + LocalMessage localMessage = (LocalMessage) message; + String accountUuid = account.getUuid(); + long messageId = localMessage.getId(); + long folderId = ((LocalFolder) localMessage.getFolder()).getId(); - if (account == null || folder == null || message == null) { - return; - } - String messKey = createMessageKey(account, folder, message); - deletedUids.put(messKey, "true"); + EmailProviderCache cache = EmailProviderCache.getCache(accountUuid, + mApplication.getApplicationContext()); + return cache.isMessageHidden(messageId, folderId); } - private void unsuppressMessage(Account account, String folder, String uid) { - if (account == null || folder == null || uid == null) { - return; - } - String messKey = createMessageKey(account, folder, uid); - deletedUids.remove(messKey); + private void setFlagInCache(final Account account, final List messageIds, + final Flag flag, final boolean newState) { + + EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(), + mApplication.getApplicationContext()); + String columnName = LocalStore.getColumnNameForFlag(flag); + String value = Integer.toString((newState) ? 1 : 0); + cache.setValueForMessages(messageIds, columnName, value); } + private void removeFlagFromCache(final Account account, final List messageIds, + final Flag flag) { - private boolean isMessageSuppressed(Account account, String folder, Message message) { - if (account == null || folder == null || message == null) { - return false; - } - String messKey = createMessageKey(account, folder, message); - - if (deletedUids.containsKey(messKey)) { - return true; - } - - return false; + EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(), + mApplication.getApplicationContext()); + String columnName = LocalStore.getColumnNameForFlag(flag); + cache.removeValueForMessages(messageIds, columnName); } + private void setFlagForThreadsInCache(final Account account, final List threadRootIds, + final Flag flag, final boolean newState) { + + EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(), + mApplication.getApplicationContext()); + String columnName = LocalStore.getColumnNameForFlag(flag); + String value = Integer.toString((newState) ? 1 : 0); + cache.setValueForThreads(threadRootIds, columnName, value); + } + + private void removeFlagForThreadsFromCache(final Account account, final List messageIds, + final Flag flag) { + + EmailProviderCache cache = EmailProviderCache.getCache(account.getUuid(), + mApplication.getApplicationContext()); + String columnName = LocalStore.getColumnNameForFlag(flag); + cache.removeValueForThreads(messageIds, columnName); + } + + /** * @param application {@link K9} */ @@ -677,7 +699,7 @@ public class MessagingController implements Runnable { public void messagesFinished(int number) {} @Override public void messageFinished(Message message, int number, int ofTotal) { - if (!isMessageSuppressed(message.getFolder().getAccount(), message.getFolder().getName(), message)) { + if (!isMessageSuppressed(message.getFolder().getAccount(), message)) { List messages = new ArrayList(); messages.add(message); @@ -1386,8 +1408,6 @@ public class MessagingController implements Runnable { if (message.isSet(Flag.DELETED)) { syncFlagMessages.add(message); return; - } else if (isMessageSuppressed(account, folder, message)) { - return; } Message localMessage = localFolder.getMessage(message.getUid()); @@ -1498,14 +1518,13 @@ public class MessagingController implements Runnable { * (POP) may not be able to give us headers for * ENVELOPE, only size. */ - if (!isMessageSuppressed(account, folder, message)) { - // keep message for delayed storing - chunk.add(message); - if (chunk.size() >= UNSYNC_CHUNK_SIZE) { - writeUnsyncedMessages(chunk, localFolder, account, folder); - chunk.clear(); - } + // keep message for delayed storing + chunk.add(message); + + if (chunk.size() >= UNSYNC_CHUNK_SIZE) { + writeUnsyncedMessages(chunk, localFolder, account, folder); + chunk.clear(); } } } catch (Exception e) { @@ -1567,15 +1586,6 @@ public class MessagingController implements Runnable { private boolean shouldImportMessage(final Account account, final String folder, final Message message, final AtomicInteger progress, final Date earliestDate) { - if (isMessageSuppressed(account, folder, message)) { - if (K9.DEBUG) { - Log.d(K9.LOG_TAG, "Message " + message.getUid() + " was suppressed " + - "but just downloaded. " + - "The race condition means we wasted some bandwidth. Oh well."); - } - return false; - - } if (account.isSearchByDateCapable() && message.olderThan(earliestDate)) { if (K9.DEBUG) { Log.d(K9.LOG_TAG, "Message " + message.getUid() + " is older than " @@ -1819,7 +1829,7 @@ public class MessagingController implements Runnable { boolean messageChanged = syncFlags(localMessage, remoteMessage); if (messageChanged) { boolean shouldBeNotifiedOf = false; - if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(account, folder, localMessage)) { + if (localMessage.isSet(Flag.DELETED) || isMessageSuppressed(account, localMessage)) { for (MessagingListener l : getListeners()) { l.synchronizeMailboxRemovedMessage(account, folder, localMessage); } @@ -2748,6 +2758,8 @@ public class MessagingController implements Runnable { public void setFlag(final Account account, final List messageIds, final Flag flag, final boolean newState) { + setFlagInCache(account, messageIds, flag, newState); + threadPool.execute(new Runnable() { @Override public void run() { @@ -2759,6 +2771,8 @@ public class MessagingController implements Runnable { public void setFlagForThreads(final Account account, final List threadRootIds, final Flag flag, final boolean newState) { + setFlagForThreadsInCache(account, threadRootIds, flag, newState); + threadPool.execute(new Runnable() { @Override public void run() { @@ -2783,8 +2797,10 @@ public class MessagingController implements Runnable { try { if (threadedList) { localStore.setFlagForThreads(ids, flag, newState); + removeFlagFromCache(account, ids, flag); } else { localStore.setFlag(ids, flag, newState); + removeFlagForThreadsFromCache(account, ids, flag); } } catch (MessagingException e) { Log.e(K9.LOG_TAG, "Couldn't set flags in local database", e); @@ -3763,9 +3779,7 @@ public class MessagingController implements Runnable { final List messages, final String destFolder, final MessagingListener listener) { - for (Message message : messages) { - suppressMessage(account, srcFolder, message); - } + suppressMessages(account, messages); putBackground("moveMessages", null, new Runnable() { @Override @@ -3779,9 +3793,7 @@ public class MessagingController implements Runnable { public void moveMessagesInThread(final Account account, final String srcFolder, final List messages, final String destFolder) { - for (Message message : messages) { - suppressMessage(account, srcFolder, message); - } + suppressMessages(account, messages); putBackground("moveMessagesInThread", null, new Runnable() { @Override @@ -3905,8 +3917,8 @@ public class MessagingController implements Runnable { for (MessagingListener l : getListeners()) { l.messageUidChanged(account, srcFolder, origUid, message.getUid()); } - unsuppressMessage(account, srcFolder, origUid); } + unsuppressMessages(account, messages); if (unreadCountAffected) { // If this move operation changes the unread count, notify the listeners @@ -3970,9 +3982,7 @@ public class MessagingController implements Runnable { public void act(final Account account, final Folder folder, final List accountMessages) { - for (Message message : accountMessages) { - suppressMessage(account, folder.getName(), message); - } + suppressMessages(account, messages); putBackground("deleteThreads", null, new Runnable() { @Override @@ -4021,9 +4031,7 @@ public class MessagingController implements Runnable { @Override public void act(final Account account, final Folder folder, final List accountMessages) { - for (Message message : accountMessages) { - suppressMessage(account, folder.getName(), message); - } + suppressMessages(account, messages); putBackground("deleteMessages", null, new Runnable() { @Override @@ -4111,9 +4119,8 @@ public class MessagingController implements Runnable { if (K9.DEBUG) Log.d(K9.LOG_TAG, "Delete policy " + account.getDeletePolicy() + " prevents delete from server"); } - for (String uid : uids) { - unsuppressMessage(account, folder, uid); - } + + unsuppressMessages(account, messages); } catch (UnavailableStorageException e) { Log.i(K9.LOG_TAG, "Failed to delete message because storage is not available - trying again later."); throw new UnavailableAccountException(e); diff --git a/src/com/fsck/k9/fragment/MessageListFragment.java b/src/com/fsck/k9/fragment/MessageListFragment.java index c48ec2a23..512f3bdd5 100644 --- a/src/com/fsck/k9/fragment/MessageListFragment.java +++ b/src/com/fsck/k9/fragment/MessageListFragment.java @@ -13,8 +13,10 @@ import java.util.Set; import java.util.concurrent.Future; import android.app.Activity; +import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.content.IntentFilter; import android.content.SharedPreferences.Editor; import android.database.Cursor; import android.graphics.Color; @@ -32,6 +34,7 @@ import android.support.v4.app.LoaderManager; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; +import android.support.v4.content.LocalBroadcastManager; import android.support.v4.widget.CursorAdapter; import android.text.Spannable; import android.text.SpannableStringBuilder; @@ -75,19 +78,19 @@ import com.fsck.k9.activity.ChooseFolder; import com.fsck.k9.activity.FolderInfoHolder; import com.fsck.k9.activity.MessageReference; import com.fsck.k9.activity.misc.ContactPictureLoader; +import com.fsck.k9.cache.EmailProviderCache; import com.fsck.k9.controller.MessagingController; -import com.fsck.k9.fragment.ConfirmationDialogFragment; import com.fsck.k9.fragment.ConfirmationDialogFragment.ConfirmationDialogFragmentListener; -import com.fsck.k9.helper.MessageHelper; import com.fsck.k9.helper.MergeCursorWithUniqueId; +import com.fsck.k9.helper.MessageHelper; import com.fsck.k9.helper.StringUtils; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.Address; import com.fsck.k9.mail.Flag; import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.Folder.OpenMode; import com.fsck.k9.mail.Message; import com.fsck.k9.mail.MessagingException; -import com.fsck.k9.mail.Folder.OpenMode; import com.fsck.k9.mail.store.LocalStore; import com.fsck.k9.mail.store.LocalStore.LocalFolder; import com.fsck.k9.provider.EmailProvider; @@ -428,6 +431,11 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick private ContactPictureLoader mContactsPictureLoader; + private LocalBroadcastManager mLocalBroadcastManager; + private BroadcastReceiver mCacheBroadcastReceiver; + private IntentFilter mCacheIntentFilter; + + /** * This class is used to run operations that modify UI elements in the UI thread. * @@ -728,7 +736,9 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - mPreferences = Preferences.getPreferences(getActivity().getApplicationContext()); + Context appContext = getActivity().getApplicationContext(); + + mPreferences = Preferences.getPreferences(appContext); mController = MessagingController.getInstance(getActivity().getApplication()); mPreviewLines = K9.messageListPreviewLines(); @@ -742,6 +752,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick restoreInstanceState(savedInstanceState); decodeArguments(); + createCacheBroadcastReceiver(appContext); + mInitialized = true; } @@ -916,6 +928,19 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick mListView.setAdapter(mAdapter); } + private void createCacheBroadcastReceiver(Context appContext) { + mLocalBroadcastManager = LocalBroadcastManager.getInstance(appContext); + + mCacheBroadcastReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + mAdapter.notifyDataSetChanged(); + } + }; + + mCacheIntentFilter = new IntentFilter(EmailProviderCache.ACTION_CACHE_UPDATED); + } + private FolderInfoHolder getFolder(String folder, Account account) { LocalFolder local_folder = null; try { @@ -960,6 +985,8 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick @Override public void onPause() { super.onPause(); + + mLocalBroadcastManager.unregisterReceiver(mCacheBroadcastReceiver); mListener.onPause(getActivity()); mController.removeListener(mListener); } @@ -1001,6 +1028,7 @@ public class MessageListFragment extends SherlockFragment implements OnItemClick } } + mLocalBroadcastManager.registerReceiver(mCacheBroadcastReceiver, mCacheIntentFilter); mListener.onResume(getActivity()); mController.addListener(mListener); diff --git a/src/com/fsck/k9/mail/store/LocalStore.java b/src/com/fsck/k9/mail/store/LocalStore.java index 69ca2aa84..aa6b51e95 100644 --- a/src/com/fsck/k9/mail/store/LocalStore.java +++ b/src/com/fsck/k9/mail/store/LocalStore.java @@ -72,6 +72,7 @@ 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.provider.EmailProvider; +import com.fsck.k9.provider.EmailProvider.MessageColumns; import com.fsck.k9.search.LocalSearch; import com.fsck.k9.search.SearchSpecification.Attribute; import com.fsck.k9.search.SearchSpecification.Searchfield; @@ -127,6 +128,28 @@ public class LocalStore extends Store implements Serializable { public static final int DB_VERSION = 47; + + public static String getColumnNameForFlag(Flag flag) { + switch (flag) { + case SEEN: { + return MessageColumns.READ; + } + case FLAGGED: { + return MessageColumns.FLAGGED; + } + case ANSWERED: { + return MessageColumns.ANSWERED; + } + case FORWARDED: { + return MessageColumns.FORWARDED; + } + default: { + throw new IllegalArgumentException("Flag must be a special column flag"); + } + } + } + + protected String uUid = null; private final Application mApplication; @@ -4104,28 +4127,7 @@ public class LocalStore extends Store implements Serializable { throws MessagingException { final ContentValues cv = new ContentValues(); - - switch (flag) { - case SEEN: { - cv.put("read", newState); - break; - } - case FLAGGED: { - cv.put("flagged", newState); - break; - } - case ANSWERED: { - cv.put("answered", newState); - break; - } - case FORWARDED: { - cv.put("forwarded", newState); - break; - } - default: { - throw new IllegalArgumentException("Flag must be a special column flag"); - } - } + cv.put(getColumnNameForFlag(flag), newState); doBatchSetSelection(new BatchSetSelection() { @@ -4173,28 +4175,7 @@ public class LocalStore extends Store implements Serializable { public void setFlagForThreads(final List threadRootIds, Flag flag, final boolean newState) throws MessagingException { - final String flagColumn; - switch (flag) { - case SEEN: { - flagColumn = "read"; - break; - } - case FLAGGED: { - flagColumn = "flagged"; - break; - } - case ANSWERED: { - flagColumn = "answered"; - break; - } - case FORWARDED: { - flagColumn = "forwarded"; - break; - } - default: { - throw new IllegalArgumentException("Flag must be a special column flag"); - } - } + final String flagColumn = getColumnNameForFlag(flag); doBatchSetSelection(new BatchSetSelection() { diff --git a/src/com/fsck/k9/provider/EmailProvider.java b/src/com/fsck/k9/provider/EmailProvider.java index 8c7729e34..4806b22ff 100644 --- a/src/com/fsck/k9/provider/EmailProvider.java +++ b/src/com/fsck/k9/provider/EmailProvider.java @@ -7,6 +7,7 @@ import java.util.Map; import com.fsck.k9.Account; import com.fsck.k9.Preferences; +import com.fsck.k9.cache.EmailProviderCacheCursor; import com.fsck.k9.helper.StringUtils; import com.fsck.k9.helper.Utility; import com.fsck.k9.mail.MessagingException; @@ -271,6 +272,7 @@ public class EmailProvider extends ContentProvider { cursor = new SpecialColumnsCursor(new IdTrickeryCursor(cursor), projection, specialColumns); + cursor = new EmailProviderCacheCursor(accountUuid, cursor, getContext()); break; } case STATS: {