From bb0f962dbbc65b97fea0dd009cac34b0561f7346 Mon Sep 17 00:00:00 2001 From: Andrew Stadler Date: Wed, 1 Jul 2009 16:04:30 -0700 Subject: [PATCH] Enable message read/unread toggling * Automatically marked read when entering MessageView * Marked unread via menu selected during MessageView * Provider updated (directly - see note) * Enable context menu in MessageList and enable "open" & toggle unread NOTE: Does not use the correct service notifications yet; Just updates the providers. NOTE: The UI for the context menu is incomplete, it says "mark as read" but it actually toggles the state. The true UI is to flip the text to match the current state e.g. "mark as read" or "mark as unread". That will be much simpler to implement when we switch to a custom list item view class, where we can cache the read/unread state (and other tidbits). --- res/menu/message_list_context.xml | 24 +++++ .../android/email/activity/MessageList.java | 93 ++++++++++++++++--- .../android/email/activity/MessageView.java | 27 ++++-- 3 files changed, 124 insertions(+), 20 deletions(-) create mode 100644 res/menu/message_list_context.xml diff --git a/res/menu/message_list_context.xml b/res/menu/message_list_context.xml new file mode 100644 index 000000000..8a3cf957c --- /dev/null +++ b/res/menu/message_list_context.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + diff --git a/src/com/android/email/activity/MessageList.java b/src/com/android/email/activity/MessageList.java index c742a4e2a..a75ae4be8 100644 --- a/src/com/android/email/activity/MessageList.java +++ b/src/com/android/email/activity/MessageList.java @@ -25,20 +25,25 @@ import com.android.email.provider.EmailContent; import com.android.email.provider.EmailContent.MessageColumns; import android.app.ListActivity; +import android.content.ContentUris; +import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.database.Cursor; import android.graphics.drawable.Drawable; +import android.net.Uri; import android.os.AsyncTask; import android.os.Bundle; import android.os.Handler; +import android.view.ContextMenu; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.Window; +import android.view.ContextMenu.ContextMenuInfo; import android.view.View.OnClickListener; import android.widget.AdapterView; import android.widget.CursorAdapter; @@ -136,18 +141,7 @@ public class MessageList extends ListActivity implements OnItemClickListener, On } public void onItemClick(AdapterView parent, View view, int position, long id) { - // TODO these can be lighter-weight lookups - EmailContent.Message message = EmailContent.Message.restoreMessageWithId(this, id); - EmailContent.Mailbox mailbox = - EmailContent.Mailbox.restoreMailboxWithId(this, message.mMailboxKey); - - if (mailbox.mType == EmailContent.Mailbox.TYPE_DRAFTS) { - // TODO need id-based API for MessageCompose - // MessageCompose.actionEditDraft(this, id); - } - else { - MessageView.actionView(this, id); - } + onOpenMessage(id); } public void onClick(View v) { @@ -182,6 +176,49 @@ public class MessageList extends ListActivity implements OnItemClickListener, On } } + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; + + // TODO: There is no context menu for the outbox + // TODO: There is probably a special context menu for the trash + + getMenuInflater().inflate(R.menu.message_list_context, menu); + + // TODO: flip the "mark as read" string if the message is already read + // In order to do this, I really should cache the read state in the item view, + // instead of re-reading data from the cursor. + } + + @Override + public boolean onContextItemSelected(MenuItem item) { + AdapterView.AdapterContextMenuInfo info = + (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); + + switch (item.getItemId()) { + case R.id.open: + onOpenMessage(info.id); + break; + case R.id.delete: + //onDelete(holder); + break; + case R.id.reply: + //onReply(holder); + break; + case R.id.reply_all: + //onReplyAll(holder); + break; + case R.id.forward: + //onForward(holder); + break; + case R.id.mark_as_read: + onToggleRead(info.id, info.targetView); + break; + } + return super.onContextItemSelected(item); + } + private void onRefresh() { // TODO: This needs to loop through all open mailboxes (there might be more than one) EmailContent.Mailbox mailbox = @@ -212,6 +249,38 @@ public class MessageList extends ListActivity implements OnItemClickListener, On AccountSettings.actionSettings(this, mailbox.mAccountKey); } + public void onOpenMessage(long messageId) { + // TODO Necessary info about the mailbox should have been cached in the listview item + // Instead, we're going to pull it from the DB here (expensively and in the wrong thread) + EmailContent.Message message = EmailContent.Message.restoreMessageWithId(this, messageId); + EmailContent.Mailbox mailbox = + EmailContent.Mailbox.restoreMailboxWithId(this, message.mMailboxKey); + + if (mailbox.mType == EmailContent.Mailbox.TYPE_DRAFTS) { + // TODO need id-based API for MessageCompose + // MessageCompose.actionEditDraft(this, messageId); + } + else { + MessageView.actionView(this, messageId); + } + } + + private void onToggleRead(long messageId, View itemView) { + // TODO the read-unread state of the given message should be cached in the listview item. + // Instead, we're going to pull it from the DB here (expensively and in the wrong thread) + EmailContent.Message message = EmailContent.Message.restoreMessageWithId(this, messageId); + boolean isRead = ! message.mFlagRead; + + // TODO this should be a call to the controller, since it may possibly kick off + // more than just a DB update. Also, the DB update shouldn't be in the UI thread + // as it is here. + ContentValues cv = new ContentValues(); + cv.put(EmailContent.MessageColumns.FLAG_READ, isRead ? 1 : 0); + Uri uri = ContentUris.withAppendedId( + EmailContent.Message.SYNCED_CONTENT_URI, messageId); + getContentResolver().update(uri, cv, null, null); + } + /** * Async task for loading a single folder out of the UI thread * diff --git a/src/com/android/email/activity/MessageView.java b/src/com/android/email/activity/MessageView.java index 717b24016..2504cf4e3 100644 --- a/src/com/android/email/activity/MessageView.java +++ b/src/com/android/email/activity/MessageView.java @@ -569,6 +569,9 @@ public class MessageView extends Activity mFavoriteIcon.setImageDrawable(newFavorite ? mFavoriteIconOn : mFavoriteIconOff); // Update provider + // TODO this should be a call to the controller, since it may possibly kick off + // more than just a DB update. Also, the DB update shouldn't be in the UI thread + // as it is here. mMessage.mFlagFavorite = newFavorite; ContentValues cv = new ContentValues(); cv.put(EmailContent.MessageColumns.FLAG_FAVORITE, newFavorite ? 1 : 0); @@ -619,13 +622,17 @@ public class MessageView extends Activity */ } - private void onMarkAsUnread() { - if (mOldMessage != null) { - MessagingController.getInstance(getApplication()).markMessageRead( - mAccount, - mFolder, - mOldMessage.getUid(), - false); + private void onMarkAsRead(boolean isRead) { + if (mMessage != null && mMessage.mFlagRead != isRead) { + // TODO this should be a call to the controller, since it may possibly kick off + // more than just a DB update. Also, the DB update shouldn't be in the UI thread + // as it is here. + mMessage.mFlagFavorite = isRead; + ContentValues cv = new ContentValues(); + cv.put(EmailContent.MessageColumns.FLAG_READ, isRead ? 1 : 0); + Uri uri = ContentUris.withAppendedId( + EmailContent.Message.SYNCED_CONTENT_URI, mMessageId); + getContentResolver().update(uri, cv, null, null); } } @@ -763,7 +770,8 @@ public class MessageView extends Activity onForward(); break; case R.id.mark_as_unread: - onMarkAsUnread(); + onMarkAsRead(false); + finish(); break; default: return false; @@ -1050,6 +1058,9 @@ public class MessageView extends Activity // toast? why would this fail? reloadBodyFromCursor(null); // hack to force text for display } + + // At this point it's fair to mark the message as "read" + onMarkAsRead(true); } }