diff --git a/res/drawable/ic_mms_answered_small.png b/res/drawable/ic_mms_answered_small.png new file mode 100644 index 000000000..2c9c69a66 Binary files /dev/null and b/res/drawable/ic_mms_answered_small.png differ diff --git a/res/menu/folder_message_list_context.xml b/res/menu/folder_message_list_context.xml index 27c45af94..a057ff8b9 100644 --- a/res/menu/folder_message_list_context.xml +++ b/res/menu/folder_message_list_context.xml @@ -24,6 +24,10 @@ android:id="@+id/mark_as_read" android:title="@string/mark_as_read_action" /> + Mark as read Forward (alternate) Choose sender + + Flag + Unflag Mark as unread Move to @@ -353,8 +356,8 @@ Welcome to K-9 Mail setup. K-9 is an open source email client for Android based Del (or D) - Delete\u000AR - Reply\u000AA - Reply All\u000AF - Forward\u000AJ or P - Previous Message\u000AK, N or space - Next Message\u000AZ - Zoom Out\u000AShift-Z - - Zoom In + Zoom In\u000aG - Flag Del (or D) - Delete\u000AR - - Reply\u000AA - Reply All\u000AC - Compose\u000AF - Forward\u000AQ + Reply\u000AA - Reply All\u000AC - Compose\u000AF - Forward\\u000aG - Flagu000AQ - Return to Accounts diff --git a/src/com/android/email/Account.java b/src/com/android/email/Account.java index 2bcbf76ae..04486d98a 100644 --- a/src/com/android/email/Account.java +++ b/src/com/android/email/Account.java @@ -51,6 +51,7 @@ public class Account implements Serializable { int mAccountNumber; boolean mVibrate; String mRingtoneUri; + boolean showOngoing = true; public enum FolderMode { ALL, FIRST_CLASS, FIRST_AND_SECOND_CLASS, NOT_SECOND_CLASS; @@ -505,4 +506,14 @@ public class Account implements Serializable { mFolderSyncMode = syncMode; } + public boolean isShowOngoing() + { + return showOngoing; + } + + public void setShowOngoing(boolean showOngoing) + { + this.showOngoing = showOngoing; + } + } diff --git a/src/com/android/email/MessagingController.java b/src/com/android/email/MessagingController.java index 4e1e10969..05a76ea93 100644 --- a/src/com/android/email/MessagingController.java +++ b/src/com/android/email/MessagingController.java @@ -87,8 +87,8 @@ public class MessagingController implements Runnable { private static final String PENDING_COMMAND_TRASH = "com.android.email.MessagingController.trash"; - private static final String PENDING_COMMAND_MARK_READ = - "com.android.email.MessagingController.markRead"; + private static final String PENDING_COMMAND_SET_FLAG = + "com.android.email.MessagingController.setFlag"; private static final String PENDING_COMMAND_APPEND = "com.android.email.MessagingController.append"; private static final String PENDING_COMMAND_MARK_ALL_AS_READ = @@ -735,16 +735,23 @@ s * critical data as fast as possible, and then we'll fill in the de fp.add(FetchProfile.Item.FLAGS); remoteFolder.fetch(remoteMessages.toArray(new Message[0]), fp, null); for (Message remoteMessage : remoteMessages) { + boolean messageChanged = false; Message localMessage = localFolder.getMessage(remoteMessage.getUid()); if (localMessage == null) { continue; } - if (!remoteMessage.isSet(Flag.X_NO_SEEN_INFO) && remoteMessage.isSet(Flag.SEEN) != localMessage.isSet(Flag.SEEN)) { - localMessage.setFlag(Flag.SEEN, remoteMessage.isSet(Flag.SEEN)); - for (MessagingListener l : getListeners()) { - l.synchronizeMailboxNewMessage(account, folder, localMessage); - } + for (Flag flag : new Flag[] { Flag.SEEN, Flag.FLAGGED, Flag.ANSWERED } ) + { + if (remoteMessage.isSet(flag) != localMessage.isSet(flag)) { + localMessage.setFlag(flag, remoteMessage.isSet(flag)); + } + } + if (messageChanged) { + for (MessagingListener l : getListeners()) { + l.synchronizeMailboxNewMessage(account, folder, localMessage); + } + } } } if (Config.LOGV) { @@ -1071,8 +1078,8 @@ s * critical data as fast as possible, and then we'll fill in the de if (PENDING_COMMAND_APPEND.equals(command.command)) { processPendingAppend(command, account); } - else if (PENDING_COMMAND_MARK_READ.equals(command.command)) { - processPendingMarkRead(command, account); + else if (PENDING_COMMAND_SET_FLAG.equals(command.command)) { + processPendingSetFlag(command, account); } else if (PENDING_COMMAND_MARK_ALL_AS_READ.equals(command.command)) { processPendingMarkAllAsRead(command, account); @@ -1286,7 +1293,7 @@ s * critical data as fast as possible, and then we'll fill in the de * @param command arguments = (String folder, String uid, boolean read) * @param account */ - private void processPendingMarkRead(PendingCommand command, Account account) + private void processPendingSetFlag(PendingCommand command, Account account) throws MessagingException { String folder = command.arguments[0]; String uid = command.arguments[1]; @@ -1296,7 +1303,9 @@ s * critical data as fast as possible, and then we'll fill in the de return; } - boolean read = Boolean.parseBoolean(command.arguments[2]); + boolean newState = Boolean.parseBoolean(command.arguments[2]); + + Flag flag = Flag.valueOf(command.arguments[3]); Store remoteStore = Store.getInstance(account.getStoreUri(), mApplication); Folder remoteFolder = remoteStore.getFolder(folder); @@ -1315,7 +1324,7 @@ s * critical data as fast as possible, and then we'll fill in the de if (remoteMessage == null) { return; } - remoteMessage.setFlag(Flag.SEEN, read); + remoteMessage.setFlag(flag, newState); } private void processPendingMarkAllAsRead(PendingCommand command, Account account) throws MessagingException { @@ -1437,6 +1446,9 @@ s * critical data as fast as possible, and then we'll fill in the de processPendingCommands(account); } + + + /** * Mark the message with the given account, folder and uid either Seen or not Seen. * @param account @@ -1449,13 +1461,27 @@ s * critical data as fast as possible, and then we'll fill in the de final String folder, final String uid, final boolean seen) { + setMessageFlag(account, folder, uid, Flag.SEEN, seen); + } + + + public void setMessageFlag( + final Account account, + final String folder, + final String uid, + final Flag flag, + boolean newState) { try { Store localStore = Store.getInstance(account.getLocalStoreUri(), mApplication); Folder localFolder = localStore.getFolder(folder); localFolder.open(OpenMode.READ_WRITE); Message message = localFolder.getMessage(uid); - message.setFlag(Flag.SEEN, seen); + message.setFlag(flag, newState); + + for (MessagingListener l : getListeners()) { + l.folderStatusChanged(account, folder); + } if (account.getErrorFolderName().equals(folder)) { @@ -1463,8 +1489,8 @@ s * critical data as fast as possible, and then we'll fill in the de } PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_MARK_READ; - command.arguments = new String[] { folder, uid, Boolean.toString(seen) }; + command.command = PENDING_COMMAND_SET_FLAG; + command.arguments = new String[] { folder, uid, Boolean.toString(newState), flag.toString() }; queuePendingCommand(account, command); processPendingCommands(account); } @@ -1951,8 +1977,8 @@ s * critical data as fast as possible, and then we'll fill in the de else if (account.getDeletePolicy() == Account.DELETE_POLICY_MARK_AS_READ) { PendingCommand command = new PendingCommand(); - command.command = PENDING_COMMAND_MARK_READ; - command.arguments = new String[] { folder, message.getUid(), Boolean.toString(true) }; + command.command = PENDING_COMMAND_SET_FLAG; + command.arguments = new String[] { folder, message.getUid(), Boolean.toString(true), Flag.SEEN.toString() }; queuePendingCommand(account, command); processPendingCommands(account); } @@ -2108,28 +2134,32 @@ s * critical data as fast as possible, and then we'll fill in the de } putBackground("sendPending " + account.getDescription(), null, new Runnable() { public void run() { - Notification notif = new Notification(R.drawable.ic_menu_refresh, - context.getString(R.string.notification_bg_send_ticker, account.getDescription()), System.currentTimeMillis()); - Intent intent = FolderMessageList.actionHandleAccountIntent(context, account, Email.INBOX); - PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0); - notif.setLatestEventInfo(context, context.getString(R.string.notification_bg_send_title), - account.getDescription() , pi); - notif.flags = Notification.FLAG_ONGOING_EVENT; - - if (Email.NOTIFICATION_LED_WHILE_SYNCING) { - notif.flags |= Notification.FLAG_SHOW_LIGHTS; - notif.ledARGB = Email.NOTIFICATION_LED_DIM_COLOR; - notif.ledOnMS = Email.NOTIFICATION_LED_FAST_ON_TIME; - notif.ledOffMS = Email.NOTIFICATION_LED_FAST_OFF_TIME; - } - - notifMgr.notify(Email.FETCHING_EMAIL_NOTIFICATION_ID, notif); + if (account.isShowOngoing()) { + Notification notif = new Notification(R.drawable.ic_menu_refresh, + context.getString(R.string.notification_bg_send_ticker, account.getDescription()), System.currentTimeMillis()); + Intent intent = FolderMessageList.actionHandleAccountIntent(context, account, Email.INBOX); + PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0); + notif.setLatestEventInfo(context, context.getString(R.string.notification_bg_send_title), + account.getDescription() , pi); + notif.flags = Notification.FLAG_ONGOING_EVENT; + + if (Email.NOTIFICATION_LED_WHILE_SYNCING) { + notif.flags |= Notification.FLAG_SHOW_LIGHTS; + notif.ledARGB = Email.NOTIFICATION_LED_DIM_COLOR; + notif.ledOnMS = Email.NOTIFICATION_LED_FAST_ON_TIME; + notif.ledOffMS = Email.NOTIFICATION_LED_FAST_OFF_TIME; + } + + notifMgr.notify(Email.FETCHING_EMAIL_NOTIFICATION_ID, notif); + } try { sendPendingMessagesSynchronous(account); } finally { - notifMgr.cancel(Email.FETCHING_EMAIL_NOTIFICATION_ID); + if (account.showOngoing) { + notifMgr.cancel(Email.FETCHING_EMAIL_NOTIFICATION_ID); + } } } } @@ -2221,29 +2251,33 @@ s * critical data as fast as possible, and then we'll fill in the de } return; } - Notification notif = new Notification(R.drawable.ic_menu_refresh, - context.getString(R.string.notification_bg_sync_ticker, account.getDescription(), folder.getName()), - System.currentTimeMillis()); - Intent intent = FolderMessageList.actionHandleAccountIntent(context, account, Email.INBOX); - PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0); - notif.setLatestEventInfo(context, context.getString(R.string.notification_bg_sync_title), account.getDescription() - + context.getString(R.string.notification_bg_title_separator) + folder.getName(), pi); - notif.flags = Notification.FLAG_ONGOING_EVENT; - if (Email.NOTIFICATION_LED_WHILE_SYNCING) { - notif.flags |= Notification.FLAG_SHOW_LIGHTS; - notif.ledARGB = Email.NOTIFICATION_LED_DIM_COLOR; - notif.ledOnMS = Email.NOTIFICATION_LED_FAST_ON_TIME; - notif.ledOffMS = Email.NOTIFICATION_LED_FAST_OFF_TIME; - } - - notifMgr.notify(Email.FETCHING_EMAIL_NOTIFICATION_ID, notif); + if (account.isShowOngoing()) { + Notification notif = new Notification(R.drawable.ic_menu_refresh, + context.getString(R.string.notification_bg_sync_ticker, account.getDescription(), folder.getName()), + System.currentTimeMillis()); + Intent intent = FolderMessageList.actionHandleAccountIntent(context, account, Email.INBOX); + PendingIntent pi = PendingIntent.getActivity(context, 0, intent, 0); + notif.setLatestEventInfo(context, context.getString(R.string.notification_bg_sync_title), account.getDescription() + + context.getString(R.string.notification_bg_title_separator) + folder.getName(), pi); + notif.flags = Notification.FLAG_ONGOING_EVENT; + if (Email.NOTIFICATION_LED_WHILE_SYNCING) { + notif.flags |= Notification.FLAG_SHOW_LIGHTS; + notif.ledARGB = Email.NOTIFICATION_LED_DIM_COLOR; + notif.ledOnMS = Email.NOTIFICATION_LED_FAST_ON_TIME; + notif.ledOffMS = Email.NOTIFICATION_LED_FAST_OFF_TIME; + } + + notifMgr.notify(Email.FETCHING_EMAIL_NOTIFICATION_ID, notif); + } try { synchronizeMailboxSynchronous(account, folder.getName()); } finally { - notifMgr.cancel(Email.FETCHING_EMAIL_NOTIFICATION_ID); + if (account.isShowOngoing()) { + notifMgr.cancel(Email.FETCHING_EMAIL_NOTIFICATION_ID); + } } } catch (Exception e) diff --git a/src/com/android/email/activity/FolderMessageList.java b/src/com/android/email/activity/FolderMessageList.java index 45d3a0d36..e702610d6 100644 --- a/src/com/android/email/activity/FolderMessageList.java +++ b/src/com/android/email/activity/FolderMessageList.java @@ -639,6 +639,7 @@ public class FolderMessageList extends ExpandableListActivity case KeyEvent.KEYCODE_F: { onForward(message); return true;} case KeyEvent.KEYCODE_A: { onReplyAll(message); return true; } case KeyEvent.KEYCODE_R: { onReply(message); return true; } + case KeyEvent.KEYCODE_G: { onToggleFlag(message); return true; } } } } @@ -832,6 +833,15 @@ public class FolderMessageList extends ExpandableListActivity // onRefresh(false); } + private void onToggleFlag(MessageInfoHolder holder) + { + + MessagingController.getInstance(getApplication()).setMessageFlag(mAccount, + holder.message.getFolder().getName(), holder.uid, Flag.FLAGGED, !holder.flagged); + holder.flagged = !holder.flagged; + mHandler.dataChanged(); + } + @Override public boolean onOptionsItemSelected(MenuItem item) { @@ -901,6 +911,9 @@ public class FolderMessageList extends ExpandableListActivity case R.id.mark_as_read: onToggleRead(holder); break; + case R.id.flag: + onToggleFlag(holder); + break; case R.id.send_alternate: onSendAlternate(mAccount, holder); break; @@ -976,7 +989,12 @@ public class FolderMessageList extends ExpandableListActivity { menu.findItem(R.id.mark_as_read).setTitle( R.string.mark_as_unread_action); - } + } + if (message.flagged) + { + menu.findItem(R.id.flag).setTitle( + R.string.unflag_action); + } } } else if (ExpandableListView.getPackedPositionType(info.packedPosition) == ExpandableListView.PACKED_POSITION_TYPE_GROUP) { @@ -1370,11 +1388,14 @@ public class FolderMessageList extends ExpandableListActivity }; private Drawable mAttachmentIcon; + private Drawable mAnsweredIcon; FolderMessageListAdapter() { mAttachmentIcon = getResources().getDrawable( R.drawable.ic_mms_attachment_small); + mAnsweredIcon = getResources().getDrawable( + R.drawable.ic_mms_answered_small); } public void removeDeletedUid(String folder, String messageUid) { @@ -1716,15 +1737,28 @@ public class FolderMessageList extends ExpandableListActivity if (message != null) { holder.chip.getBackground().setAlpha(message.read ? 0 : 255); + + holder.subject.setTypeface(null, + message.read && !message.flagged ? Typeface.NORMAL : Typeface.BOLD); + + if (message.flagged) + { + holder.subject.setTextColor(0xffff4444); + } + else + { + holder.subject.setTextColor(0xffffffff); + } holder.subject.setText(message.subject); - holder.subject.setTypeface(null, message.read ? Typeface.NORMAL - : Typeface.BOLD); + holder.from.setText(message.sender); - holder.from.setTypeface(null, message.read ? Typeface.NORMAL - : Typeface.BOLD); + holder.from.setTypeface(null, message.read ? Typeface.NORMAL : Typeface.BOLD); holder.date.setText(message.date); - holder.subject.setCompoundDrawablesWithIntrinsicBounds(null, null, - message.hasAttachments ? mAttachmentIcon : null, null); + holder.subject.setCompoundDrawablesWithIntrinsicBounds( + message.answered ? mAnsweredIcon : null, // left + null, // top + message.hasAttachments ? mAttachmentIcon : null, // right + null); // bottom } else { @@ -1804,12 +1838,18 @@ public class FolderMessageList extends ExpandableListActivity public Date compareDate; public String sender; + + public String[] recipients; public boolean hasAttachments; public String uid; public boolean read; + + public boolean answered; + + public boolean flagged; public Message message; @@ -1837,6 +1877,8 @@ public class FolderMessageList extends ExpandableListActivity } this.hasAttachments = message.getAttachmentCount() > 0; this.read = message.isSet(Flag.SEEN); + this.answered = message.isSet(Flag.ANSWERED); + this.flagged = message.isSet(Flag.FLAGGED); if (folder.outbox) { this.sender = Address.toFriendly(message diff --git a/src/com/android/email/activity/MessageCompose.java b/src/com/android/email/activity/MessageCompose.java index b1bfce2aa..99d5f44ae 100644 --- a/src/com/android/email/activity/MessageCompose.java +++ b/src/com/android/email/activity/MessageCompose.java @@ -47,6 +47,7 @@ import com.android.email.R; import com.android.email.Utility; import com.android.email.mail.Address; import com.android.email.mail.Body; +import com.android.email.mail.Flag; import com.android.email.mail.Message; import com.android.email.mail.MessagingException; import com.android.email.mail.Multipart; @@ -199,7 +200,7 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus Context context, Account account, Message message, - boolean replyAll) { + boolean replyAll) { Intent i = new Intent(context, MessageCompose.class); i.putExtra(EXTRA_ACCOUNT, account); i.putExtra(EXTRA_FOLDER, message.getFolder().getName()); @@ -394,6 +395,15 @@ public class MessageCompose extends Activity implements OnClickListener, OnFocus mFolder = (String) intent.getStringExtra(EXTRA_FOLDER); mSourceMessageUid = (String) intent.getStringExtra(EXTRA_MESSAGE); } + + Log.d(Email.LOG_TAG, "action = " + action + ", mAccount = " + mAccount + ", mFolder = " + mFolder + ", mSourceMessageUid = " + mSourceMessageUid); + if ((ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)) && mAccount != null && mFolder != null && mSourceMessageUid != null) + { + Log.d(Email.LOG_TAG, "Setting message ANSWERED flag to true"); + // TODO: Really, we should wait until we send the message, but that would require saving the original + // message info along with a Draft copy, in case it is left in Drafts for a while before being sent + MessagingController.getInstance(getApplication()).setMessageFlag(mAccount, mFolder, mSourceMessageUid, Flag.ANSWERED, true); + } if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action) || ACTION_FORWARD.equals(action) || ACTION_EDIT_DRAFT.equals(action)) { diff --git a/src/com/android/email/activity/MessageView.java b/src/com/android/email/activity/MessageView.java index 78e3b6a77..4ed91553c 100644 --- a/src/com/android/email/activity/MessageView.java +++ b/src/com/android/email/activity/MessageView.java @@ -55,6 +55,7 @@ import com.android.email.MessagingListener; import com.android.email.R; import com.android.email.Utility; import com.android.email.mail.Address; +import com.android.email.mail.Flag; import com.android.email.mail.Message; import com.android.email.mail.MessagingException; import com.android.email.mail.Multipart; @@ -132,6 +133,7 @@ public class MessageView extends Activity case KeyEvent.KEYCODE_F: { onForward(); return true;} case KeyEvent.KEYCODE_A: { onReplyAll(); return true; } case KeyEvent.KEYCODE_R: { onReply(); return true; } + case KeyEvent.KEYCODE_G: { onFlag(); return true; } case KeyEvent.KEYCODE_J: case KeyEvent.KEYCODE_P: { onPrevious(); return true; } @@ -492,6 +494,22 @@ public class MessageView extends Activity } } + private void onFlag() { + if (mMessage != null) { + MessagingController.getInstance(getApplication()).setMessageFlag(mAccount, + mMessage.getFolder().getName(), mMessage.getUid(), Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED)); + try + { + mMessage.setFlag(Flag.FLAGGED, !mMessage.isSet(Flag.FLAGGED)); + } + catch (MessagingException me) + { + Log.e(Email.LOG_TAG, "Could not set flag on local message", me); + } + } + } + + private void onSendAlternate() { if (mMessage != null) { MessagingController.getInstance(getApplication()).sendAlternate(this, mAccount, mMessage); diff --git a/src/com/android/email/mail/store/ImapStore.java b/src/com/android/email/mail/store/ImapStore.java index d1689225d..14dabb20d 100644 --- a/src/com/android/email/mail/store/ImapStore.java +++ b/src/com/android/email/mail/store/ImapStore.java @@ -1100,6 +1100,13 @@ public class ImapStore extends Store { else if (flag == Flag.DELETED) { flagNames.add("\\Deleted"); } + else if (flag == Flag.ANSWERED) { + flagNames.add("\\Answered"); + } + else if (flag == Flag.FLAGGED) { + flagNames.add("\\Flagged"); + } + } try { mConnection.executeSimpleCommand(String.format("UID STORE 1:* %sFLAGS.SILENT (%s)", @@ -1127,6 +1134,12 @@ public class ImapStore extends Store { else if (flag == Flag.DELETED) { flagNames.add("\\Deleted"); } + else if (flag == Flag.ANSWERED) { + flagNames.add("\\Answered"); + } + else if (flag == Flag.FLAGGED) { + flagNames.add("\\Flagged"); + } } try { mConnection.executeSimpleCommand(String.format("UID STORE %s %sFLAGS.SILENT (%s)",