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).
This commit is contained in:
Andrew Stadler 2009-07-01 16:04:30 -07:00
parent 48c8cc34b7
commit bb0f962dbb
3 changed files with 124 additions and 20 deletions

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2009 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/open" android:title="@string/open_action" />
<item android:id="@+id/delete" android:title="@string/delete_action" />
<item android:id="@+id/forward" android:title="@string/forward_action" />
<item android:id="@+id/reply_all" android:title="@string/reply_all_action" />
<item android:id="@+id/reply" android:title="@string/reply_action" />
<item android:id="@+id/mark_as_read" android:title="@string/mark_as_read_action" />
</menu>

View File

@ -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
*

View File

@ -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);
}
}