From 8cee3ee18d946fe3f82c3da3367f03c7234491e0 Mon Sep 17 00:00:00 2001 From: Danny Baumann Date: Wed, 2 Jan 2013 14:07:41 +0100 Subject: [PATCH] Add actions to notifications. - If there's only a single message in the notification, add 'Reply' and 'Read' (marks as read) buttons. - If there's more than one message pending, add only 'Read'. --- AndroidManifest.xml | 5 + res/values/strings.xml | 3 + src/com/fsck/k9/activity/MessageCompose.java | 36 +++++-- .../k9/controller/MessagingController.java | 8 ++ .../k9/service/NotificationActionService.java | 102 ++++++++++++++++++ 5 files changed, 145 insertions(+), 9 deletions(-) create mode 100644 src/com/fsck/k9/service/NotificationActionService.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index 49e18eb01..8e675b193 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -360,6 +360,11 @@ otherwise it would make K-9 start at the wrong time android:enabled="true" > + + %d Unread (%s) + %d more on %s + Reply + Read + Checking mail: %s:%s Checking mail Sending mail: %s diff --git a/src/com/fsck/k9/activity/MessageCompose.java b/src/com/fsck/k9/activity/MessageCompose.java index e6f53637f..0b7049585 100644 --- a/src/com/fsck/k9/activity/MessageCompose.java +++ b/src/com/fsck/k9/activity/MessageCompose.java @@ -389,6 +389,32 @@ public class MessageCompose extends K9Activity implements OnClickListener { context.startActivity(i); } + /** + * Get intent for composing a new message as a reply to the given message. If replyAll is true + * the function is reply all instead of simply reply. + * @param context + * @param account + * @param message + * @param replyAll + * @param messageBody optional, for decrypted messages, null if it should be grabbed from the given message + */ + public static Intent getActionReplyIntent( + Context context, + Account account, + Message message, + boolean replyAll, + String messageBody) { + Intent i = new Intent(context, MessageCompose.class); + i.putExtra(EXTRA_MESSAGE_BODY, messageBody); + i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference()); + if (replyAll) { + i.setAction(ACTION_REPLY_ALL); + } else { + i.setAction(ACTION_REPLY); + } + return i; + } + /** * Compose a new message as a reply to the given message. If replyAll is true the function * is reply all instead of simply reply. @@ -404,15 +430,7 @@ public class MessageCompose extends K9Activity implements OnClickListener { Message message, boolean replyAll, String messageBody) { - Intent i = new Intent(context, MessageCompose.class); - i.putExtra(EXTRA_MESSAGE_BODY, messageBody); - i.putExtra(EXTRA_MESSAGE_REFERENCE, message.makeMessageReference()); - if (replyAll) { - i.setAction(ACTION_REPLY_ALL); - } else { - i.setAction(ACTION_REPLY); - } - context.startActivity(i); + context.startActivity(getActionReplyIntent(context, account, message, replyAll, messageBody)); } /** diff --git a/src/com/fsck/k9/controller/MessagingController.java b/src/com/fsck/k9/controller/MessagingController.java index 33383d49e..4aa72d60e 100644 --- a/src/com/fsck/k9/controller/MessagingController.java +++ b/src/com/fsck/k9/controller/MessagingController.java @@ -81,6 +81,7 @@ import com.fsck.k9.search.LocalSearch; import com.fsck.k9.search.SearchAccount; import com.fsck.k9.search.SearchSpecification; import com.fsck.k9.search.SqlQueryBuilder; +import com.fsck.k9.service.NotificationActionService; /** @@ -4615,7 +4616,14 @@ public class MessagingController implements Runnable { builder.setSubText(accountDescr); builder.setContentTitle(sender); builder.setStyle(style); + + builder.addAction(R.drawable.ic_action_single_message_options_dark, + context.getString(R.string.notification_action_reply), + NotificationActionService.getReplyIntent(context, account, message)); } + builder.addAction(R.drawable.ic_action_mark_as_read_dark, + context.getString(R.string.notification_action_read), + NotificationActionService.getReadAllMessagesIntent(context, account, data.messages)); } else { String accountNotice = context.getString(R.string.notification_new_one_account_fmt, unreadCount, accountDescr); diff --git a/src/com/fsck/k9/service/NotificationActionService.java b/src/com/fsck/k9/service/NotificationActionService.java new file mode 100644 index 000000000..8d900a3c7 --- /dev/null +++ b/src/com/fsck/k9/service/NotificationActionService.java @@ -0,0 +1,102 @@ +package com.fsck.k9.service; + +import java.util.ArrayList; +import java.util.Collection; + +import com.fsck.k9.Account; +import com.fsck.k9.K9; +import com.fsck.k9.Preferences; +import com.fsck.k9.activity.MessageCompose; +import com.fsck.k9.activity.MessageReference; +import com.fsck.k9.controller.MessagingController; +import com.fsck.k9.mail.Flag; +import com.fsck.k9.mail.Folder; +import com.fsck.k9.mail.Message; +import com.fsck.k9.mail.MessagingException; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.util.Log; + +public class NotificationActionService extends CoreService { + private final static String REPLY_ACTION = "com.fsck.k9.service.NotificationActionService.REPLY_ACTION"; + private final static String READ_ALL_ACTION = "com.fsck.k9.service.NotificationActionService.READ_ALL_ACTION"; + + private final static String EXTRA_ACCOUNT = "account"; + private final static String EXTRA_MESSAGE = "message"; + private final static String EXTRA_MESSAGE_IDS = "message_ids"; + + public static PendingIntent getReplyIntent(Context context, final Account account, final Message message) { + Intent i = new Intent(context, NotificationActionService.class); + i.putExtra(EXTRA_ACCOUNT, account.getUuid()); + i.putExtra(EXTRA_MESSAGE, message.makeMessageReference()); + i.setAction(REPLY_ACTION); + + return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT); + } + + public static PendingIntent getReadAllMessagesIntent(Context context, final Account account, final Collection messages) { + ArrayList messageIds = new ArrayList(); + + for (Message m : messages) { + messageIds.add(m.getId()); + } + + Intent i = new Intent(context, NotificationActionService.class); + i.putExtra(EXTRA_ACCOUNT, account.getUuid()); + i.putExtra(EXTRA_MESSAGE_IDS, messageIds); + i.setAction(READ_ALL_ACTION); + + return PendingIntent.getService(context, account.getAccountNumber(), i, PendingIntent.FLAG_UPDATE_CURRENT); + } + + @Override + public int startService(Intent intent, int startId) { + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "NotificationActionService started with startId = " + startId); + final Preferences preferences = Preferences.getPreferences(this); + final MessagingController controller = MessagingController.getInstance(getApplication()); + final Account account = preferences.getAccount(intent.getStringExtra(EXTRA_ACCOUNT)); + + if (account != null) { + if (READ_ALL_ACTION.equals(intent.getAction())) { + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "NotificationActionService marking messages as read"); + + ArrayList messageIds = (ArrayList) intent.getSerializableExtra(EXTRA_MESSAGE_IDS); + + controller.setFlag(account, messageIds, Flag.SEEN, true, false); + } else if (REPLY_ACTION.equals(intent.getAction())) { + if (K9.DEBUG) + Log.i(K9.LOG_TAG, "NotificationActionService initiating reply"); + + try { + MessageReference ref = (MessageReference) intent.getParcelableExtra(EXTRA_MESSAGE); + Folder folder = account.getLocalStore().getFolder(ref.folderName); + if (folder != null) { + Message message = folder.getMessage(ref.uid); + if (message != null) { + Intent i = MessageCompose.getActionReplyIntent(this, account, message, false, null); + i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + startActivity(i); + } else { + Log.w(K9.LOG_TAG, "Could not find message for notification action."); + } + } else { + Log.w(K9.LOG_TAG, "Could not find folder for notification action."); + } + } catch (MessagingException e) { + Log.w(K9.LOG_TAG, "Could not execute reply action.", e); + } + } + + /* there's no point in keeping the notification after the user clicked on it */ + controller.notifyAccountCancel(this, account); + } else { + Log.w(K9.LOG_TAG, "Could not find account for notification action."); + } + + return START_NOT_STICKY; + } +}