mirror of
https://github.com/moparisthebest/k-9
synced 2024-12-25 00:58:50 -05:00
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.
This commit is contained in:
parent
9287d97626
commit
50fcff76fd
179
src/com/fsck/k9/cache/EmailProviderCache.java
vendored
Normal file
179
src/com/fsck/k9/cache/EmailProviderCache.java
vendored
Normal file
@ -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<String, EmailProviderCache> sInstances =
|
||||
new HashMap<String, EmailProviderCache>();
|
||||
|
||||
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<Long, Map<String, String>> mMessageCache = new HashMap<Long, Map<String, String>>();
|
||||
private Map<Long, Map<String, String>> mThreadCache = new HashMap<Long, Map<String, String>>();
|
||||
private Map<Long, Long> mHiddenMessageCache = new HashMap<Long, Long>();
|
||||
|
||||
|
||||
private EmailProviderCache(String accountUuid) {
|
||||
mAccountUuid = accountUuid;
|
||||
}
|
||||
|
||||
public String getValueForMessage(Long messageId, String columnName) {
|
||||
synchronized (mMessageCache) {
|
||||
Map<String, String> map = mMessageCache.get(messageId);
|
||||
return (map == null) ? null : map.get(columnName);
|
||||
}
|
||||
}
|
||||
|
||||
public String getValueForThread(Long threadRootId, String columnName) {
|
||||
synchronized (mThreadCache) {
|
||||
Map<String, String> map = mThreadCache.get(threadRootId);
|
||||
return (map == null) ? null : map.get(columnName);
|
||||
}
|
||||
}
|
||||
|
||||
public void setValueForMessages(List<Long> messageIds, String columnName, String value) {
|
||||
synchronized (mMessageCache) {
|
||||
for (Long messageId : messageIds) {
|
||||
Map<String, String> map = mMessageCache.get(messageId);
|
||||
if (map == null) {
|
||||
map = new HashMap<String, String>();
|
||||
mMessageCache.put(messageId, map);
|
||||
}
|
||||
map.put(columnName, value);
|
||||
}
|
||||
}
|
||||
|
||||
notifyChange();
|
||||
}
|
||||
|
||||
public void setValueForThreads(List<Long> threadRootIds, String columnName, String value) {
|
||||
synchronized (mThreadCache) {
|
||||
for (Long threadRootId : threadRootIds) {
|
||||
Map<String, String> map = mThreadCache.get(threadRootId);
|
||||
if (map == null) {
|
||||
map = new HashMap<String, String>();
|
||||
mThreadCache.put(threadRootId, map);
|
||||
}
|
||||
map.put(columnName, value);
|
||||
}
|
||||
}
|
||||
|
||||
notifyChange();
|
||||
}
|
||||
|
||||
public void removeValueForMessages(List<Long> messageIds, String columnName) {
|
||||
synchronized (mMessageCache) {
|
||||
for (Long messageId : messageIds) {
|
||||
Map<String, String> map = mMessageCache.get(messageId);
|
||||
if (map != null) {
|
||||
map.remove(columnName);
|
||||
if (map.size() == 0) {
|
||||
mMessageCache.remove(messageId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void removeValueForThreads(List<Long> threadRootIds, String columnName) {
|
||||
synchronized (mThreadCache) {
|
||||
for (Long threadRootId : threadRootIds) {
|
||||
Map<String, String> map = mThreadCache.get(threadRootId);
|
||||
if (map != null) {
|
||||
map.remove(columnName);
|
||||
if (map.size() == 0) {
|
||||
mThreadCache.remove(threadRootId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void hideMessages(List<Message> 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.
|
||||
*
|
||||
* <p><strong>Note:</strong>
|
||||
* 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.
|
||||
* </p>
|
||||
*/
|
||||
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);
|
||||
}
|
||||
}
|
129
src/com/fsck/k9/cache/EmailProviderCacheCursor.java
vendored
Normal file
129
src/com/fsck/k9/cache/EmailProviderCacheCursor.java
vendored
Normal file
@ -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<Integer> mHiddenRows = new ArrayList<Integer>();
|
||||
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;
|
||||
}
|
||||
}
|
@ -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<String, String> deletedUids = new ConcurrentHashMap<String, String>();
|
||||
|
||||
/**
|
||||
* 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<Message> 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<Long> 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<Long> 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<Long> 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<Long> 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<Message> messages = new ArrayList<Message>();
|
||||
|
||||
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<Long> 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<Long> 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<Message> 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<Message> 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<Message> 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<Message> 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);
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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<Long> 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() {
|
||||
|
||||
|
@ -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: {
|
||||
|
Loading…
Reference in New Issue
Block a user