Remove notifications on reply/forward action

Clear the notifications when the user selects the reply/reply-all/
forward action.

Also fixes a bug with those actions not always doing the right thing.

Bug: 7742529
Change-Id: I479eef30864fe1fcae3e9c7aafaa21481e51d05c
diff --git a/src/com/android/mail/MailIntentService.java b/src/com/android/mail/MailIntentService.java
new file mode 100644
index 0000000..e1e5d82
--- /dev/null
+++ b/src/com/android/mail/MailIntentService.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+package com.android.mail;
+
+import android.app.IntentService;
+import android.content.Intent;
+
+/**
+ * A service to handle various intents asynchronously.
+ */
+public class MailIntentService extends IntentService {
+    public static final String ACTION_RESEND_NOTIFICATIONS =
+            "com.android.mail.action.RESEND_NOTIFICATIONS";
+    public static final String ACTION_CLEAR_NEW_MAIL_NOTIFICATIONS =
+            "com.android.mail.action.CLEAR_NEW_MAIL_NOTIFICATIONS";
+
+    public static final String CLEAR_NEW_MAIL_NOTIFICATIONS_ACCOUNT_EXTRA = "account";
+    public static final String CLEAR_NEW_MAIL_NOTIFICATIONS_FOLDER_EXTRA = "folder";
+    public static final String CLEAR_NEW_MAIL_NOTIFICATIONS_MARK_SEEN_EXTRA = "markSeen";
+
+    public MailIntentService() {
+        super("MailIntentService");
+    }
+
+    protected MailIntentService(final String name) {
+        super(name);
+    }
+
+    @Override
+    protected void onHandleIntent(final Intent intent) {
+        // UnifiedEmail does not handle any of these at the moment
+    }
+}
diff --git a/src/com/android/mail/NotificationActionIntentService.java b/src/com/android/mail/NotificationActionIntentService.java
index aabd117..7813ce7 100644
--- a/src/com/android/mail/NotificationActionIntentService.java
+++ b/src/com/android/mail/NotificationActionIntentService.java
@@ -16,6 +16,8 @@
 package com.android.mail;
 
 import android.app.IntentService;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
 import android.content.ContentResolver;
 import android.content.ContentValues;
 import android.content.Context;
@@ -23,8 +25,11 @@
 import android.net.Uri;
 import android.os.Bundle;
 
+import com.android.mail.providers.Account;
+import com.android.mail.providers.Folder;
 import com.android.mail.providers.Message;
 import com.android.mail.providers.UIProvider;
+import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.NotificationActionUtils;
 import com.android.mail.utils.NotificationActionUtils.NotificationAction;
 
@@ -32,6 +37,12 @@
  * Processes notification action {@link Intent}s that need to run off the main thread.
  */
 public class NotificationActionIntentService extends IntentService {
+    private static final String TAG = "NotificationActionIntentService";
+
+    // Compose actions
+    public static final String ACTION_REPLY = "com.android.mail.action.NOTIF_REPLY";
+    public static final String ACTION_REPLY_ALL = "com.android.mail.action.NOTIF_REPLY_ALL";
+    public static final String ACTION_FORWARD = "com.android.mail.action.NOTIF_FORWARD";
     // Toggle actions
     public static final String ACTION_MARK_READ = "com.android.mail.action.NOTIF_MARK_READ";
 
@@ -52,6 +63,8 @@
 
     public static final String EXTRA_NOTIFICATION_ACTION =
             "com.android.mail.extra.EXTRA_NOTIFICATION_ACTION";
+    public static final String EXTRA_NOTIFICATION_PENDING_INTENT =
+            "com.android.mail.extra.EXTRA_NOTIFICATION_PENDING_INTENT";
     public static final String ACTION_UNDO_TIMEOUT = "com.android.mail.action.NOTIF_UNDO_TIMEOUT";
 
     public NotificationActionIntentService() {
@@ -78,10 +91,24 @@
 
             NotificationActionUtils.registerUndoTimeout(context, notificationAction);
         } else {
+            final Account account = notificationAction.getAccount();
+            final String accountName = account.name;
+
             if (ACTION_UNDO_TIMEOUT.equals(action) || ACTION_DESTRUCT.equals(action)) {
                 // Process the action
                 NotificationActionUtils.cancelUndoTimeout(this, notificationAction);
                 NotificationActionUtils.processUndoNotification(this, notificationAction);
+            } else if (ACTION_REPLY.equals(action) || ACTION_REPLY_ALL.equals(action)
+                    || ACTION_FORWARD.equals(action)) {
+                cancelNotifications(accountName, notificationAction.getFolder());
+
+                final PendingIntent pendingIntent =
+                        intent.getParcelableExtra(EXTRA_NOTIFICATION_PENDING_INTENT);
+                try {
+                    pendingIntent.send();
+                } catch (final CanceledException e) {
+                    LogUtils.e(TAG, "Error replying from notification action", e);
+                }
             } else if (ACTION_MARK_READ.equals(action)) {
                 final Uri uri = message.uri;
 
@@ -94,4 +121,12 @@
             NotificationActionUtils.resendNotifications(context);
         }
     }
+
+    private void cancelNotifications(final String account, final Folder folder) {
+        final Intent intent = new Intent(MailIntentService.ACTION_CLEAR_NEW_MAIL_NOTIFICATIONS);
+        intent.putExtra(MailIntentService.CLEAR_NEW_MAIL_NOTIFICATIONS_ACCOUNT_EXTRA, account);
+        intent.putExtra(MailIntentService.CLEAR_NEW_MAIL_NOTIFICATIONS_FOLDER_EXTRA, folder);
+
+        startService(intent);
+    }
 }
diff --git a/src/com/android/mail/utils/NotificationActionUtils.java b/src/com/android/mail/utils/NotificationActionUtils.java
index b8b165b..1182561 100644
--- a/src/com/android/mail/utils/NotificationActionUtils.java
+++ b/src/com/android/mail/utils/NotificationActionUtils.java
@@ -34,6 +34,7 @@
 import android.text.format.DateUtils;
 import android.widget.RemoteViews;
 
+import com.android.mail.MailIntentService;
 import com.android.mail.NotificationActionIntentService;
 import com.android.mail.R;
 import com.android.mail.compose.ComposeActivity;
@@ -70,9 +71,6 @@
      */
     public static final SparseLongArray sNotificationTimestamps = new SparseLongArray();
 
-    public static final String ACTION_RESEND_NOTIFICATIONS =
-            "com.android.mail.action.RESEND_NOTIFICATIONS";
-
     public enum NotificationActionType {
         REPLY("reply", R.drawable.ic_reply_holo_dark, R.string.notification_action_reply),
         REPLY_ALL("reply_all", R.drawable.ic_reply_all_holo_dark,
@@ -331,8 +329,19 @@
 
                 taskStackBuilder.addNextIntent(notificationIntent).addNextIntent(replyIntent);
 
-                return taskStackBuilder.getPendingIntent(
+                final PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(
                         notificationId, PendingIntent.FLAG_UPDATE_CURRENT);
+
+                final String intentAction = NotificationActionIntentService.ACTION_REPLY;
+
+                final Intent intent = new Intent(intentAction);
+                intent.putExtra(NotificationActionIntentService.EXTRA_NOTIFICATION_ACTION,
+                        notificationAction);
+                intent.putExtra(NotificationActionIntentService.EXTRA_NOTIFICATION_PENDING_INTENT,
+                        pendingIntent);
+
+                return PendingIntent.getService(
+                        context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
             } case REPLY_ALL: {
                 // Build a task stack that forces the conversation view on the stack before the
                 // reply activity.
@@ -342,13 +351,24 @@
                 // To make sure that the reply intents one notification don't clobber over
                 // intents for other notification, force a data uri on the intent
                 final Uri notificationUri =
-                        Uri.parse("gmailfrom://gmail-ls/account/" + "reply/" + notificationId);
+                        Uri.parse("gmailfrom://gmail-ls/account/" + "replyall/" + notificationId);
                 replyIntent.setData(notificationUri);
 
                 taskStackBuilder.addNextIntent(notificationIntent).addNextIntent(replyIntent);
 
-                return taskStackBuilder.getPendingIntent(
+                final PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(
                         notificationId, PendingIntent.FLAG_UPDATE_CURRENT);
+
+                final String intentAction = NotificationActionIntentService.ACTION_REPLY_ALL;
+
+                final Intent intent = new Intent(intentAction);
+                intent.putExtra(NotificationActionIntentService.EXTRA_NOTIFICATION_ACTION,
+                        notificationAction);
+                intent.putExtra(NotificationActionIntentService.EXTRA_NOTIFICATION_PENDING_INTENT,
+                        pendingIntent);
+
+                return PendingIntent.getService(
+                        context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
             } case FORWARD: {
                 // Build a task stack that forces the conversation view on the stack before the
                 // reply activity.
@@ -358,13 +378,24 @@
                 // To make sure that the reply intents one notification don't clobber over
                 // intents for other notification, force a data uri on the intent
                 final Uri notificationUri =
-                        Uri.parse("gmailfrom://gmail-ls/account/" + "reply/" + notificationId);
+                        Uri.parse("gmailfrom://gmail-ls/account/" + "forward/" + notificationId);
                 replyIntent.setData(notificationUri);
 
                 taskStackBuilder.addNextIntent(notificationIntent).addNextIntent(replyIntent);
 
-                return taskStackBuilder.getPendingIntent(
+                final PendingIntent pendingIntent = taskStackBuilder.getPendingIntent(
                         notificationId, PendingIntent.FLAG_UPDATE_CURRENT);
+
+                final String intentAction = NotificationActionIntentService.ACTION_FORWARD;
+
+                final Intent intent = new Intent(intentAction);
+                intent.putExtra(NotificationActionIntentService.EXTRA_NOTIFICATION_ACTION,
+                        notificationAction);
+                intent.putExtra(NotificationActionIntentService.EXTRA_NOTIFICATION_PENDING_INTENT,
+                        pendingIntent);
+
+                return PendingIntent.getService(
+                        context, notificationId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
             } case ARCHIVE_REMOVE_LABEL: {
                 final String intentAction =
                         NotificationActionIntentService.ACTION_ARCHIVE_REMOVE_LABEL;
@@ -680,7 +711,7 @@
      */
     public static void createUndoNotification(final Context context,
             final NotificationAction notificationAction) {
-        final int notificationId = getUndoNotificationId(
+        final int notificationId = getNotificationId(
                 notificationAction.getAccount().name, notificationAction.getFolder());
 
         final Notification notification =
@@ -696,7 +727,7 @@
 
     public static void cancelUndoNotification(final Context context,
             final NotificationAction notificationAction) {
-        sUndoNotifications.delete(getUndoNotificationId(
+        sUndoNotifications.delete(getNotificationId(
                 notificationAction.getAccount().name, notificationAction.getFolder()));
         resendNotifications(context);
     }
@@ -707,7 +738,7 @@
      */
     public static void processUndoNotification(final Context context,
             final NotificationAction notificationAction) {
-        final int notificationId = getUndoNotificationId(
+        final int notificationId = getNotificationId(
                 notificationAction.getAccount().name, notificationAction.getFolder());
         sUndoNotifications.delete(notificationId);
         sNotificationTimestamps.delete(notificationId);
@@ -715,7 +746,7 @@
         resendNotifications(context);
     }
 
-    private static int getUndoNotificationId(final String account, final Folder folder) {
+    public static int getNotificationId(final String account, final Folder folder) {
         // TODO(skennedy): When notifications are fully in UnifiedEmail, remove this method and use
         // the one in Utils
         // 1 == Gmail.NOTIFICATION_ID
@@ -726,7 +757,7 @@
      * Broadcasts an {@link Intent} to inform the app to resend its notifications.
      */
     public static void resendNotifications(final Context context) {
-        final Intent intent = new Intent(ACTION_RESEND_NOTIFICATIONS);
+        final Intent intent = new Intent(MailIntentService.ACTION_RESEND_NOTIFICATIONS);
         intent.setPackage(context.getPackageName()); // Make sure we only deliver this to ourself
         context.startService(intent);
     }
diff --git a/src/com/android/mail/utils/Utils.java b/src/com/android/mail/utils/Utils.java
index 40f3b83..3ab91ac 100644
--- a/src/com/android/mail/utils/Utils.java
+++ b/src/com/android/mail/utils/Utils.java
@@ -16,6 +16,12 @@
 
 package com.android.mail.utils;
 
+import com.google.android.common.html.parser.HtmlDocument;
+import com.google.android.common.html.parser.HtmlParser;
+import com.google.android.common.html.parser.HtmlTree;
+import com.google.android.common.html.parser.HtmlTreeBuilder;
+import com.google.common.collect.Maps;
+
 import android.app.SearchManager;
 import android.content.Context;
 import android.content.Intent;
@@ -28,7 +34,6 @@
 import android.os.Build;
 import android.os.Bundle;
 import android.provider.Browser;
-import android.text.Html;
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
@@ -49,16 +54,12 @@
 
 import com.android.mail.R;
 import com.android.mail.browse.ConversationCursor;
+import com.android.mail.compose.ComposeActivity;
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
 import com.android.mail.providers.UIProvider;
 import com.android.mail.providers.UIProvider.EditSettingsExtras;
-import com.google.android.common.html.parser.HtmlDocument;
-import com.google.android.common.html.parser.HtmlParser;
-import com.google.android.common.html.parser.HtmlTree;
-import com.google.android.common.html.parser.HtmlTreeBuilder;
-import com.google.common.collect.Maps;
 
 import org.json.JSONObject;
 
@@ -1161,4 +1162,22 @@
         return count;
     }
 
+    /**
+     * @return an intent which, if launched, will reply to the conversation
+     */
+    public static Intent createReplyIntent(final Context context, final Account account,
+            final Uri messageUri, final boolean isReplyAll) {
+        final Intent intent =
+                ComposeActivity.createReplyIntent(context, account, messageUri, isReplyAll);
+        return intent;
+    }
+
+    /**
+     * @return an intent which, if launched, will forward the conversation
+     */
+    public static Intent createForwardIntent(
+            final Context context, final Account account, final Uri messageUri) {
+        final Intent intent = ComposeActivity.createForwardIntent(context, account, messageUri);
+        return intent;
+    }
 }