Refactor suppression of deleted messages into MessagingController.

Re-enable instant deletes in MessageView

Deleted message suppression should be generalized into a flag cache,
serving Seen, Flagged, and Answered flags, as well.

Copy message to Trash still needs work, so that no duplicates are ever
visible and server-sync is more clean.  This will be especially
important, as the same code will be needed for the future "message
copy/move" facility.
This commit is contained in:
Daniel Applebaum 2009-01-29 13:35:43 +00:00
parent 9fe3656391
commit fe2dfb76d0
3 changed files with 184 additions and 111 deletions

View File

@ -14,6 +14,7 @@ import java.util.Iterator;
import java.util.List;
import java.util.Set;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
@ -112,8 +113,84 @@ public class MessagingController implements Runnable {
private boolean mBusy;
private Application mApplication;
// Key is accountUuid:folderName:messageUid , value is unimportant
private ConcurrentHashMap<String, String> deletedUids = new ConcurrentHashMap<String, String>();
// Key is accountUuid:folderName , value is a long of the highest message UID ever emptied from Trash
private ConcurrentHashMap<String, Long> expungedUid = new ConcurrentHashMap<String, Long>();
private String createMessageKey(Account account, String folder, Message message)
{
return account.getUuid() + ":" + folder + ":" + message.getUid();
}
private String createFolderKey(Account account, String folder)
{
return account.getUuid() + ":" + folder;
}
private void suppressMessage(Account account, String folder, Message message)
{
if (account == null || folder == null || message == null)
{
return;
}
String messKey = createMessageKey(account, folder, message);
Log.d(Email.LOG_TAG, "Suppressing message with key " + messKey);
deletedUids.put(messKey, "true");
}
private void unsuppressMessage(Account account, String folder, Message message)
{
if (account == null || folder == null || message == null)
{
return;
}
String messKey = createMessageKey(account, folder, message);
Log.d(Email.LOG_TAG, "Unsuppressing message with key " + messKey);
deletedUids.remove(messKey);
}
private boolean isMessageSuppressed(Account account, String folder, Message message)
{
if (account == null || folder == null || message == null)
{
return false;
}
String messKey = createMessageKey(account, folder, message);
Log.d(Email.LOG_TAG, "Checking suppression of message with key " + messKey);
if (deletedUids.containsKey(messKey))
{
Log.d(Email.LOG_TAG, "Message with key " + messKey + " is suppressed");
return true;
}
Long expungedUidL = expungedUid.get(createFolderKey(account, folder));
if (expungedUidL != null)
{
long expungedUid = expungedUidL;
String messageUidS = message.getUid();
try
{
long messageUid = Long.parseLong(messageUidS);
if (messageUid <= expungedUid)
{
return false;
}
}
catch (NumberFormatException nfe)
{
// Nothing to do
}
}
return false;
}
private MessagingController(Application application) {
mApplication = application;
mThread = new Thread(this);
@ -380,7 +457,9 @@ public class MessagingController implements Runnable {
Message[] localMessages = localFolder.getMessages(null);
ArrayList<Message> messages = new ArrayList<Message>();
for (Message message : localMessages) {
if (!message.isSet(Flag.DELETED)) {
if (!message.isSet(Flag.DELETED) &&
isMessageSuppressed(account, localFolder.getName(), message) == false) {
messages.add(message);
}
}
@ -689,10 +768,13 @@ s * critical data as fast as possible, and then we'll fill in the de
* (POP) may not be able to give us headers for
* ENVELOPE, only size.
*/
if (isMessageSuppressed(account, folder, message) == false)
{
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxNewMessage(account, folder,
localFolder.getMessage(message.getUid()));
}
}
}
}
@ -740,7 +822,8 @@ s * critical data as fast as possible, and then we'll fill in the de
messageChanged = true;
}
}
if (messageChanged) {
if (messageChanged && isMessageSuppressed(account, folder, localMessage) == false)
{
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxNewMessage(account, folder, localMessage);
}
@ -825,13 +908,15 @@ s * critical data as fast as possible, and then we'll fill in the de
// Set a flag indicating this message has now be fully downloaded
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
// Update the listener with what we've found
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxNewMessage(
account,
folder,
localMessage);
if (isMessageSuppressed(account, folder, localMessage) == false)
{
// Update the listener with what we've found
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxNewMessage(
account,
folder,
localMessage);
}
}
}
catch (MessagingException me) {
@ -919,13 +1004,15 @@ s * critical data as fast as possible, and then we'll fill in the de
// viewed.
localMessage.setFlag(Flag.X_DOWNLOADED_FULL, true);
}
// Update the listener with what we've found
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxNewMessage(
account,
folder,
localFolder.getMessage(message.getUid()));
if (isMessageSuppressed(account, folder, message) == false)
{
// Update the listener with what we've found
for (MessagingListener l : getListeners()) {
l.synchronizeMailboxNewMessage(
account,
folder,
localFolder.getMessage(message.getUid()));
}
}
}
if (Config.LOGV) {
@ -1898,6 +1985,8 @@ s * critical data as fast as possible, and then we'll fill in the de
public void deleteMessage(final Account account, final String folder, final Message message,
final MessagingListener listener) {
suppressMessage(account, folder, message);
put("deleteMessage", null, new Runnable() {
public void run() {
deleteMessageSynchronous(account, folder, message, listener);
@ -1907,53 +1996,60 @@ s * critical data as fast as possible, and then we'll fill in the de
private void deleteMessageSynchronous(final Account account, final String folder, final Message message,
MessagingListener listener) {
account.getUuid();
message.getUid();
try {
Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication);
Folder localFolder = localStore.getFolder(folder);
Message lMessage = localFolder.getMessage(message.getUid());
if (folder.equals(account.getTrashFolderName()))
if (lMessage != null)
{
if (Config.LOGD)
{
Log.d(Email.LOG_TAG, "Deleting message in trash folder, not copying");
}
lMessage.setFlag(Flag.DELETED, true);
}
else
{
Folder localTrashFolder = localStore.getFolder(account.getTrashFolderName());
if (localTrashFolder.exists() == false)
{
localTrashFolder.create(Folder.FolderType.HOLDS_MESSAGES);
}
if (localTrashFolder.exists() == true)
if (folder.equals(account.getTrashFolderName()))
{
if (Config.LOGD)
{
Log.d(Email.LOG_TAG, "Deleting message in normal folder, copying");
Log.d(Email.LOG_TAG, "Deleting message in trash folder, not copying");
}
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(FetchProfile.Item.BODY);
// TODO: Turn the fetch/copy/delete into an atomic move
localFolder.fetch(new Message[] { lMessage }, fp, null);
localFolder.copyMessages(new Message[] { lMessage }, localTrashFolder);
if (folder.equals(account.getOutboxFolderName())) {
lMessage.setFlag(Flag.X_DESTROYED, true);
lMessage.setFlag(Flag.DELETED, true);
}
else
{
Folder localTrashFolder = localStore.getFolder(account.getTrashFolderName());
if (localTrashFolder.exists() == false)
{
localTrashFolder.create(Folder.FolderType.HOLDS_MESSAGES);
}
else {
lMessage.setFlag(Flag.DELETED, true);
if (localTrashFolder.exists() == true)
{
if (Config.LOGD)
{
Log.d(Email.LOG_TAG, "Deleting message in normal folder, copying");
}
FetchProfile fp = new FetchProfile();
fp.add(FetchProfile.Item.ENVELOPE);
fp.add(FetchProfile.Item.BODY);
// TODO: Turn the fetch/copy/delete into an atomic move
localFolder.fetch(new Message[] { lMessage }, fp, null);
localFolder.copyMessages(new Message[] { lMessage }, localTrashFolder);
if (folder.equals(account.getOutboxFolderName())) {
lMessage.setFlag(Flag.X_DESTROYED, true);
}
else {
lMessage.setFlag(Flag.DELETED, true);
}
}
}
}
localFolder.close(false);
unsuppressMessage(account, folder, message);
if (listener != null) {
listener.messageDeleted(account, folder, message);
}
// for (MessagingListener l : getListeners()) {
// l.folderStatusChanged(account, account.getTrashFolderName());
// }
for (MessagingListener l : getListeners()) {
l.folderStatusChanged(account, account.getTrashFolderName());
}
if (Config.LOGD)
{

View File

@ -395,8 +395,7 @@ public class FolderMessageList extends ExpandableListActivity
// Lower our priority
Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
// Synchronously load the list of local messages
MessagingController.getInstance(getApplication()).listLocalMessages(
mAccount, mFolder, mAdapter.mListener);
try
{
Store localStore = Store.getInstance(mAccount.getLocalStoreUri(),
@ -418,7 +417,12 @@ public class FolderMessageList extends ExpandableListActivity
// at it's leisure
MessagingController.getInstance(getApplication()).synchronizeMailbox(
mAccount, mFolder, mAdapter.mListener);
}
}
else
{
MessagingController.getInstance(getApplication()).listLocalMessages(
mAccount, mFolder, mAdapter.mListener);
}
}
}
@ -611,8 +615,9 @@ public class FolderMessageList extends ExpandableListActivity
final FolderInfoHolder folder = (FolderInfoHolder) mAdapter
.getGroup(groupPosition);
if (folder.messages.size() == 0)
if (folder.messages.size() == 0 || folder.needsRefresh)
{
folder.needsRefresh = false;
new Thread(new FolderUpdateWorker(folder.name, false)).start();
}
}
@ -771,24 +776,16 @@ public class FolderMessageList extends ExpandableListActivity
{
holder.folder.unreadMessageCount--;
}
FolderInfoHolder trashHolder = mAdapter.getFolder(mAccount.getTrashFolderName());
if (trashHolder != null)
{
trashHolder.needsRefresh = true;
}
mAdapter.removeMessage(holder.message.getFolder().getName(), holder.uid);
if (holder.folder.name.equals(mAccount.getTrashFolderName()) == false) {
mAdapter.addOrUpdateMessage(mAccount.getTrashFolderName(), holder.message);
}
MessagingController.getInstance(getApplication()).deleteMessage(mAccount,
holder.message.getFolder().getName(), holder.message,
new MessagingListener() {
@Override
public void messageDeleted(Account account, String folder, Message message) {
if (account != mAccount) {
return;
}
mAdapter.removeDeletedUid(folder, message.getUid());
}
}
);
holder.message.getFolder().getName(), holder.message, null);
}
@ -832,7 +829,6 @@ public class FolderMessageList extends ExpandableListActivity
public void controllerCommandCompleted(boolean moreToDo)
{
Log.v(Email.LOG_TAG, "Empty Trash background task completed");
mAdapter.removeAllDeletedUids(account.getTrashFolderName());
}
};
@ -1280,9 +1276,6 @@ public class FolderMessageList extends ExpandableListActivity
return;
}
FolderInfoHolder holder = getFolder(folder);
// if (holder != null) {
// holder.deletedUids.clear();
// }
mHandler.progress(false);
mHandler.folderLoading(folder, false);
// mHandler.folderStatus(folder, null);
@ -1311,7 +1304,6 @@ public class FolderMessageList extends ExpandableListActivity
if (holder != null)
{
holder.lastChecked = 0;
// holder.deletedUids.clear();
}
mHandler.folderSyncing(null);
}
@ -1327,7 +1319,17 @@ public class FolderMessageList extends ExpandableListActivity
addOrUpdateMessage(folder, message);
}
@Override
public void messageDeleted(Account account,
String folder, Message message)
{
synchronizeMailboxRemovedMessage(account,
folder, message);
}
@Override
public void synchronizeMailboxRemovedMessage(Account account,
String folder, Message message)
{
@ -1421,23 +1423,6 @@ public class FolderMessageList extends ExpandableListActivity
mAnsweredIcon = getResources().getDrawable(
R.drawable.ic_mms_answered_small);
}
public void removeDeletedUid(String folder, String messageUid) {
FolderInfoHolder f = getFolder(folder);
if (f != null)
{
f.deletedUids.remove(messageUid);
}
}
public void removeAllDeletedUids(String folder)
{
FolderInfoHolder f = getFolder(folder);
if (f != null)
{
f.deletedUids.clear();
}
}
public void removeAllMessages(String folder)
{
@ -1465,9 +1450,7 @@ public class FolderMessageList extends ExpandableListActivity
{
return;
}
if (f.deletedUids.contains(messageUid) == false) {
f.deletedUids.add(messageUid);
}
mHandler.removeMessage(f, m);
}
@ -1489,9 +1472,6 @@ public class FolderMessageList extends ExpandableListActivity
private void addOrUpdateMessage(FolderInfoHolder folder, Message message,
boolean sort, boolean notify)
{
if (folder.deletedUids.contains(message.getUid())){
return;
}
MessageInfoHolder m = getMessage(folder, message.getUid());
if (m == null)
{
@ -1855,7 +1835,8 @@ public class FolderMessageList extends ExpandableListActivity
public boolean lastCheckFailed;
public List<String> deletedUids = Collections.synchronizedList(new ArrayList<String>());
public boolean needsRefresh = false;
/**
* Outbox is handled differently from any other folder.
*/

View File

@ -528,27 +528,19 @@ public class MessageView extends Activity
findSurroundingMessagesUid();
MessagingListener listener = new MessagingListener()
{
public void messageDeleted(Account account, String folder, Message message)
{
if (mNextMessageUid != null) {
onNext();
}
else if (mPreviousMessageUid != null) {
onPrevious();
} else {
finish();
}
}
};
MessagingListener waitListener = listener;
MessagingController.getInstance(getApplication()).deleteMessage(
accountForDelete,
folderForDelete,
messageToDelete,
waitListener);
null);
if (mNextMessageUid != null) {
onNext();
}
else if (mPreviousMessageUid != null) {
onPrevious();
} else {
finish();
}
}
@ -847,12 +839,16 @@ public class MessageView extends Activity
private void renderAttachments(Part part, int depth) throws MessagingException {
String contentType = MimeUtility.unfoldAndDecode(part.getContentType());
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
String name = MimeUtility.getHeaderParameter(contentType, "name");
if (name == null)
{
name = MimeUtility.getHeaderParameter(contentDisposition, "filename");
}
if (name != null) {
/*
* We're guaranteed size because LocalStore.fetch puts it there.
*/
String contentDisposition = MimeUtility.unfoldAndDecode(part.getDisposition());
int size = Integer.parseInt(MimeUtility.getHeaderParameter(contentDisposition, "size"));
Attachment attachment = new Attachment();