Made sure the message list is only modified from the UI thread

This commit is contained in:
cketti 2012-09-05 03:02:22 +02:00
parent d97da517fa
commit 16ab1b67bc
1 changed files with 115 additions and 100 deletions

View File

@ -360,6 +360,7 @@ public class MessageList
private static final int ACTION_FOLDER_LOADING = 5; private static final int ACTION_FOLDER_LOADING = 5;
private static final int ACTION_REFRESH_TITLE = 6; private static final int ACTION_REFRESH_TITLE = 6;
private static final int ACTION_PROGRESS = 7; private static final int ACTION_PROGRESS = 7;
private static final int ACTION_REMOVE_MESSAGE = 8;
/** /**
@ -405,6 +406,37 @@ public class MessageList
sendMessage(msg); sendMessage(msg);
} }
public void removeMessage(MessageReference messageReference) {
android.os.Message msg = android.os.Message.obtain(this, ACTION_REMOVE_MESSAGE,
messageReference);
sendMessage(msg);
}
public void changeMessageUid(final MessageReference ref, final String newUid) {
// Instead of creating a container to be able to pass both arguments in a Message we
// post a Runnable to the message queue.
post(new Runnable() {
@Override
public void run() {
mAdapter.changeMessageUid(ref, newUid);
}
});
}
public void addOrUpdateMessages(final Account account, final String folderName,
final List<Message> providedMessages, final boolean verifyAgainstSearch) {
// We copy the message list because it's later modified by MessagingController
final List<Message> messages = new ArrayList<Message>(providedMessages);
post(new Runnable() {
@Override
public void run() {
mAdapter.addOrUpdateMessages(account, folderName, messages,
verifyAgainstSearch);
}
});
}
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
@Override @Override
public void handleMessage(android.os.Message msg) { public void handleMessage(android.os.Message msg) {
@ -442,6 +474,11 @@ public class MessageList
MessageList.this.progress(progress); MessageList.this.progress(progress);
break; break;
} }
case ACTION_REMOVE_MESSAGE: {
MessageReference messageReference = (MessageReference) msg.obj;
mAdapter.removeMessage(messageReference);
break;
}
} }
} }
} }
@ -838,22 +875,21 @@ public class MessageList
// reread the selected date format preference in case it has changed // reread the selected date format preference in case it has changed
mMessageHelper.refresh(); mMessageHelper.refresh();
mAdapter.markAllMessagesAsDirty();
new Thread() { new Thread() {
@Override @Override
public void run() { public void run() {
mAdapter.markAllMessagesAsDirty();
if (mFolderName != null) { if (mFolderName != null) {
mController.listLocalMessagesSynchronous(mAccount, mFolderName, mAdapter.mListener); mController.listLocalMessagesSynchronous(mAccount, mFolderName, mAdapter.mListener);
} else if (mQueryString != null) { } else if (mQueryString != null) {
mController.searchLocalMessagesSynchronous(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener); mController.searchLocalMessagesSynchronous(mAccountUuids, mFolderNames, null, mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mAdapter.mListener);
} }
mAdapter.pruneDirtyMessages();
runOnUiThread(new Runnable() { runOnUiThread(new Runnable() {
@Override @Override
public void run() { public void run() {
mAdapter.pruneDirtyMessages();
mAdapter.notifyDataSetChanged(); mAdapter.notifyDataSetChanged();
restoreListState(); restoreListState();
} }
@ -1905,17 +1941,12 @@ public class MessageList
@Override @Override
public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) { public void synchronizeMailboxAddOrUpdateMessage(Account account, String folder, Message message) {
addOrUpdateMessage(account, folder, message, true); mHandler.addOrUpdateMessages(account, folder, Collections.singletonList(message), true);
} }
@Override @Override
public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) { public void synchronizeMailboxRemovedMessage(Account account, String folder, Message message) {
MessageInfoHolder holder = getMessage(message); mHandler.removeMessage(message.makeMessageReference());
if (holder == null) {
Log.w(K9.LOG_TAG, "Got callback to remove non-existent message with UID " + message.getUid());
} else {
mHandler.removeMessages(Collections.singletonList(holder));
}
} }
@Override @Override
@ -1952,20 +1983,17 @@ public class MessageList
@Override @Override
public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) { public void listLocalMessagesRemoveMessage(Account account, String folder, Message message) {
MessageInfoHolder holder = getMessage(message); mHandler.removeMessage(message.makeMessageReference());
if (holder != null) {
mHandler.removeMessages(Collections.singletonList(holder));
}
} }
@Override @Override
public void listLocalMessagesAddMessages(Account account, String folder, List<Message> messages) { public void listLocalMessagesAddMessages(Account account, String folder, List<Message> messages) {
addOrUpdateMessages(account, folder, messages, false); mHandler.addOrUpdateMessages(account, folder, messages, false);
} }
@Override @Override
public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) { public void listLocalMessagesUpdateMessage(Account account, String folder, Message message) {
addOrUpdateMessage(account, folder, message, false); mHandler.addOrUpdateMessages(account, folder, Collections.singletonList(message), false);
} }
@Override @Override
@ -1989,11 +2017,7 @@ public class MessageList
ref.folderName = folder; ref.folderName = folder;
ref.uid = oldUid; ref.uid = oldUid;
MessageInfoHolder holder = getMessage(ref); mHandler.changeMessageUid(ref, newUid);
if (holder != null) {
holder.uid = newUid;
holder.message.setUid(newUid);
}
} }
}; };
@ -2032,27 +2056,26 @@ public class MessageList
} }
public void pruneDirtyMessages() { public void pruneDirtyMessages() {
synchronized (mMessages) { List<MessageInfoHolder> messagesToRemove = new ArrayList<MessageInfoHolder>();
Iterator<MessageInfoHolder> iter = mMessages.iterator();
while (iter.hasNext()) { for (MessageInfoHolder holder : mMessages) {
MessageInfoHolder holder = iter.next(); if (holder.dirty) {
if (holder.dirty) { messagesToRemove.add(holder);
if (holder.selected) {
mSelectedCount--;
toggleBatchButtons();
}
mHandler.removeMessages(Collections.singletonList(holder));
}
} }
} }
removeMessages(messagesToRemove);
}
public void removeMessage(MessageReference messageReference) {
MessageInfoHolder holder = getMessage(messageReference);
if (holder == null) {
Log.w(K9.LOG_TAG, "Got callback to remove non-existent message with UID " +
messageReference.uid);
} else {
removeMessages(Collections.singletonList(holder));
}
} }
/**
* Note: Must be called from the UI thread!
*
* @param holders
* Never {@code null}.
*/
public void removeMessages(final List<MessageInfoHolder> messages) { public void removeMessages(final List<MessageInfoHolder> messages) {
if (messages.isEmpty()) { if (messages.isEmpty()) {
return; return;
@ -2074,11 +2097,6 @@ public class MessageList
toggleBatchButtons(); toggleBatchButtons();
} }
/**
* Note: Must be called from the UI thread!
*
* @param messages
*/
public void addMessages(final List<MessageInfoHolder> messages) { public void addMessages(final List<MessageInfoHolder> messages) {
if (messages.isEmpty()) { if (messages.isEmpty()) {
return; return;
@ -2088,10 +2106,7 @@ public class MessageList
for (final MessageInfoHolder message : messages) { for (final MessageInfoHolder message : messages) {
if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) { if (mFolderName == null || (message.folder != null && message.folder.name.equals(mFolderName))) {
int index; int index = Collections.binarySearch(mMessages, message, getComparator());
synchronized (mMessages) {
index = Collections.binarySearch(mMessages, message, getComparator());
}
if (index < 0) { if (index < 0) {
index = (index * -1) - 1; index = (index * -1) - 1;
@ -2109,45 +2124,37 @@ public class MessageList
notifyDataSetChanged(); notifyDataSetChanged();
} }
/** public void changeMessageUid(MessageReference ref, String newUid) {
* Note: Must be called from the UI thread! MessageInfoHolder holder = getMessage(ref);
*/ if (holder != null) {
holder.uid = newUid;
holder.message.setUid(newUid);
}
}
public void resetUnreadCount() { public void resetUnreadCount() {
if (mQueryString != null) { if (mQueryString != null) {
int unreadCount = 0; int unreadCount = 0;
synchronized (mMessages) {
for (MessageInfoHolder holder : mMessages) { for (MessageInfoHolder holder : mMessages) {
unreadCount += holder.read ? 0 : 1; unreadCount += holder.read ? 0 : 1;
}
} }
mUnreadMessageCount = unreadCount; mUnreadMessageCount = unreadCount;
refreshTitle(); refreshTitle();
} }
} }
/**
* Note: Must be called from the UI thread!
*/
public void sortMessages() { public void sortMessages() {
final Comparator<MessageInfoHolder> chainComparator = getComparator(); final Comparator<MessageInfoHolder> chainComparator = getComparator();
synchronized (mMessages) { Collections.sort(mMessages, chainComparator);
Collections.sort(mMessages, chainComparator);
}
notifyDataSetChanged(); notifyDataSetChanged();
} }
private void addOrUpdateMessage(Account account, String folderName, Message message, boolean verifyAgainstSearch) { public void addOrUpdateMessages(final Account account, final String folderName,
List<Message> messages = new ArrayList<Message>(); final List<Message> messages, final boolean verifyAgainstSearch) {
messages.add(message);
addOrUpdateMessages(account, folderName, messages, verifyAgainstSearch);
}
private void addOrUpdateMessages(final Account account, final String folderName, final List<Message> providedMessages, final boolean verifyAgainstSearch) {
// we copy the message list because the callback doesn't expect
// the callbacks to mutate it.
final List<Message> messages = new ArrayList<Message>(providedMessages);
boolean needsSort = false; boolean needsSort = false;
final List<MessageInfoHolder> messagesToAdd = new ArrayList<MessageInfoHolder>(); final List<MessageInfoHolder> messagesToAdd = new ArrayList<MessageInfoHolder>();
@ -2169,7 +2176,9 @@ public class MessageList
if (m == null) { if (m == null) {
if (updateForMe(account, folderName)) { if (updateForMe(account, folderName)) {
m = new MessageInfoHolder(); m = new MessageInfoHolder();
messageHelper.populate(m, message, new FolderInfoHolder(MessageList.this, messageFolder, messageAccount), messageAccount); FolderInfoHolder folderInfoHolder = new FolderInfoHolder(
MessageList.this, messageFolder, messageAccount);
messageHelper.populate(m, message, folderInfoHolder, messageAccount);
messagesToAdd.add(m); messagesToAdd.add(m);
} else { } else {
if (mQueryString != null) { if (mQueryString != null) {
@ -2177,63 +2186,71 @@ public class MessageList
messagesToSearch.add(message); messagesToSearch.add(message);
} else { } else {
m = new MessageInfoHolder(); m = new MessageInfoHolder();
messageHelper.populate(m, message, new FolderInfoHolder(MessageList.this, messageFolder, messageAccount), messageAccount); FolderInfoHolder folderInfoHolder = new FolderInfoHolder(
MessageList.this, messageFolder, messageAccount);
messageHelper.populate(m, message, folderInfoHolder,
messageAccount);
messagesToAdd.add(m); messagesToAdd.add(m);
} }
} }
} }
} else { } else {
m.dirty = false; // as we reload the message, unset its dirty flag m.dirty = false; // as we reload the message, unset its dirty flag
messageHelper.populate(m, message, new FolderInfoHolder(MessageList.this, messageFolder, account), account); FolderInfoHolder folderInfoHolder = new FolderInfoHolder(MessageList.this,
messageFolder, account);
messageHelper.populate(m, message, folderInfoHolder, account);
needsSort = true; needsSort = true;
} }
} }
} }
if (!messagesToSearch.isEmpty()) { if (!messagesToSearch.isEmpty()) {
mController.searchLocalMessages(mAccountUuids, mFolderNames, messagesToSearch.toArray(EMPTY_MESSAGE_ARRAY), mQueryString, mIntegrate, mQueryFlags, mForbiddenFlags, mController.searchLocalMessages(mAccountUuids, mFolderNames,
messagesToSearch.toArray(EMPTY_MESSAGE_ARRAY), mQueryString, mIntegrate,
mQueryFlags, mForbiddenFlags,
new MessagingListener() { new MessagingListener() {
@Override @Override
public void listLocalMessagesAddMessages(Account account, String folder, List<Message> messages) { public void listLocalMessagesAddMessages(Account account, String folder,
addOrUpdateMessages(account, folder, messages, false); List<Message> messages) {
mHandler.addOrUpdateMessages(account, folder, messages, false);
} }
}); });
} }
if (!messagesToRemove.isEmpty()) { if (!messagesToRemove.isEmpty()) {
mHandler.removeMessages(messagesToRemove); removeMessages(messagesToRemove);
} }
if (!messagesToAdd.isEmpty()) { if (!messagesToAdd.isEmpty()) {
mHandler.addMessages(messagesToAdd); addMessages(messagesToAdd);
} }
if (needsSort) { if (needsSort) {
mHandler.sortMessages(); sortMessages();
mHandler.resetUnreadCount(); resetUnreadCount();
} }
} }
public MessageInfoHolder getMessage(Message message) {
private MessageInfoHolder getMessage(Message message) {
return getMessage(message.makeMessageReference()); return getMessage(message.makeMessageReference());
} }
// XXX TODO - make this not use a for loop // XXX TODO - make this not use a for loop
public MessageInfoHolder getMessage(MessageReference messageReference) { private MessageInfoHolder getMessage(MessageReference messageReference) {
synchronized (mMessages) { for (MessageInfoHolder holder : mMessages) {
for (MessageInfoHolder holder : mMessages) { /*
/* * 2010-06-21 - cketti
* 2010-06-21 - cketti * Added null pointer check. Not sure what's causing 'holder'
* Added null pointer check. Not sure what's causing 'holder' * to be null. See log provided in issue 1749, comment #15.
* to be null. See log provided in issue 1749, comment #15. *
* * Please remove this comment once the cause was found and the
* Please remove this comment once the cause was found and the * bug(?) fixed.
* bug(?) fixed. */
*/ if ((holder != null) && holder.message.equalsReference(messageReference)) {
if ((holder != null) && holder.message.equalsReference(messageReference)) { return holder;
return holder;
}
} }
} }
return null; return null;
} }
@ -2287,10 +2304,8 @@ public class MessageList
@Override @Override
public Object getItem(int position) { public Object getItem(int position) {
try { try {
synchronized (mMessages) { if (position < mMessages.size()) {
if (position < mMessages.size()) { return mMessages.get(position);
return mMessages.get(position);
}
} }
} catch (Exception e) { } catch (Exception e) {
Log.e(K9.LOG_TAG, "getItem(" + position + "), but folder.messages.size() = " + mMessages.size(), e); Log.e(K9.LOG_TAG, "getItem(" + position + "), but folder.messages.size() = " + mMessages.size(), e);