Merge "Use a single view for the accountswitchspinner." into jb-ub-mail
diff --git a/res/menu/conversation_actions.xml b/res/menu/conversation_actions.xml
index 60599f6..99cd4ec 100644
--- a/res/menu/conversation_actions.xml
+++ b/res/menu/conversation_actions.xml
@@ -68,6 +68,12 @@
         android:id="@+id/report_spam"
         android:title="@string/report_spam"
         android:icon="@drawable/ic_spam_normal_holo_light" />
+    <!-- Depends on AccountCapabilities.REPORT_SPAM -->
+    <item
+        android:id="@+id/mark_not_spam"
+        android:title="@string/mark_not_spam"
+        android:icon="@drawable/ic_spam_normal_holo_light"
+        android:visible="false" />
 
     <!-- Always available -->
     <item android:id="@+id/settings"
diff --git a/res/menu/conversation_list_selection_actions_menu.xml b/res/menu/conversation_list_selection_actions_menu.xml
index a0d9b2a..1c1d60d 100644
--- a/res/menu/conversation_list_selection_actions_menu.xml
+++ b/res/menu/conversation_list_selection_actions_menu.xml
@@ -86,5 +86,11 @@
         android:title="@string/report_spam"
         android:showAsAction="never"
         android:icon="@drawable/ic_spam_normal_holo_light" />
+    <!-- Availability based on account -->
+    <item
+        android:id="@+id/mark_not_spam"
+        android:title="@string/mark_not_spam"
+        android:showAsAction="never"
+        android:icon="@drawable/ic_spam_normal_holo_light" />
 
 </menu>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index d9c5014..dbf709e 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -113,8 +113,10 @@
     <string name="remove_star">Remove star</string>
     <!-- Menu item: archive this conversation -->
     <string name="archive">Archive</string>
-    <!-- Menu item: report this conversation as spam -->
+    <!-- Menu item: report this conversation as spam [CHAR LIMIT = 30] -->
     <string name="report_spam">Report spam</string>
+    <!-- Menu item: report this conversation not as spam [CHAR LIMIT = 30] -->
+    <string name="mark_not_spam">Not spam</string>
     <!-- Menu item: delete this conversation -->
     <string name="delete">Delete</string>
     <!-- Menu item: next conversation -->
diff --git a/src/com/android/mail/browse/ConversationCursor.java b/src/com/android/mail/browse/ConversationCursor.java
index 23a2cda..4bd3594 100644
--- a/src/com/android/mail/browse/ConversationCursor.java
+++ b/src/com/android/mail/browse/ConversationCursor.java
@@ -1184,6 +1184,7 @@
         public static final int ARCHIVE = 3;
         public static final int MUTE = 4;
         public static final int REPORT_SPAM = 5;
+        public static final int REPORT_NOT_SPAM = 6;
         public static final int MOSTLY_ARCHIVE = MOSTLY | ARCHIVE;
         public static final int MOSTLY_DELETE = MOSTLY | DELETE;
 
@@ -1294,12 +1295,16 @@
                             .build();
                     break;
                 case REPORT_SPAM:
+                case REPORT_NOT_SPAM:
                     sProvider.deleteLocal(mUri, ConversationCursor.this);
 
+                    final String operation = mType == REPORT_SPAM ?
+                            ConversationOperations.REPORT_SPAM :
+                            ConversationOperations.REPORT_NOT_SPAM;
+
                     // Create an update operation that represents report spam
                     op = ContentProviderOperation.newUpdate(uri).withValue(
-                            ConversationOperations.OPERATION_KEY,
-                            ConversationOperations.REPORT_SPAM).build();
+                            ConversationOperations.OPERATION_KEY, operation).build();
                     break;
                 default:
                     throw new UnsupportedOperationException(
@@ -1638,6 +1643,13 @@
     }
 
     /**
+     * As above, for report not spam
+     */
+    public int reportNotSpam(Context context, Collection<Conversation> conversations) {
+        return applyAction(context, conversations, ConversationOperation.REPORT_NOT_SPAM);
+    }
+
+    /**
      * As above, for mostly archive
      */
     public int mostlyArchive(Context context, Collection<Conversation> conversations) {
diff --git a/src/com/android/mail/browse/SelectedConversationsActionMenu.java b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
index 07bed28..ab93bf6 100644
--- a/src/com/android/mail/browse/SelectedConversationsActionMenu.java
+++ b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
@@ -119,6 +119,12 @@
             case R.id.report_spam:
                 mUpdater.delete(mSelectionSet.values(), mUpdater.getBatchAction(R.id.report_spam));
                 break;
+            case R.id.mark_not_spam:
+                // Currently, since spam messages are only shown in list with other spam messages,
+                // marking a message not as spam is a destructive action
+                mUpdater.delete(mSelectionSet.values(),
+                        mUpdater.getBatchAction(R.id.mark_not_spam));
+                break;
             case R.id.read:
                 markConversationsRead(true);
                 break;
@@ -305,6 +311,7 @@
         boolean showStar = false;
         boolean showMarkUnread = false;
         boolean showMarkImportant = false;
+        boolean showMarkNotSpam = false;
 
         for (Conversation conversation : conversations) {
             if (!conversation.starred) {
@@ -316,7 +323,10 @@
             if (!conversation.isImportant()) {
                 showMarkImportant = true;
             }
-            if (showStar && showMarkUnread && showMarkImportant) {
+            if (conversation.spam) {
+                showMarkNotSpam = true;
+            }
+            if (showStar && showMarkUnread && showMarkImportant && showMarkNotSpam) {
                 break;
             }
         }
@@ -333,7 +343,11 @@
                 mFolder.supportsCapability(FolderCapabilities.ARCHIVE));
         final MenuItem spam = menu.findItem(R.id.report_spam);
         spam.setVisible(mAccount.supportsCapability(UIProvider.AccountCapabilities.REPORT_SPAM) &&
-                mFolder.supportsCapability(FolderCapabilities.ARCHIVE));
+                mFolder.supportsCapability(FolderCapabilities.REPORT_SPAM));
+        final MenuItem notSpam = menu.findItem(R.id.mark_not_spam);
+        notSpam.setVisible(showMarkNotSpam &&
+                mAccount.supportsCapability(UIProvider.AccountCapabilities.REPORT_SPAM) &&
+                mFolder.supportsCapability(FolderCapabilities.MARK_NOT_SPAM));
         final MenuItem mute = menu.findItem(R.id.mute);
         mute.setVisible(mAccount.supportsCapability(UIProvider.AccountCapabilities.MUTE));
         final MenuItem markImportant = menu.findItem(R.id.mark_important);
diff --git a/src/com/android/mail/providers/UIProvider.java b/src/com/android/mail/providers/UIProvider.java
index f2b7c0b..9b61314 100644
--- a/src/com/android/mail/providers/UIProvider.java
+++ b/src/com/android/mail/providers/UIProvider.java
@@ -173,34 +173,38 @@
          */
         public static final int REPORT_SPAM = 0x0002;
         /**
+         * Whether the server allows reporting phishing back.
+         */
+        public static final int REPORT_PHISHING = 0x0004;
+        /**
          * Whether the server supports a concept of Archive: removing mail from the Inbox but
          * keeping it around.
          */
-        public static final int ARCHIVE = 0x0004;
+        public static final int ARCHIVE = 0x0008;
         /**
          * Whether the server will stop notifying on updates to this thread? This requires
          * THREADED_CONVERSATIONS to be true, otherwise it should be ignored.
          */
-        public static final int MUTE = 0x0008;
+        public static final int MUTE = 0x0010;
         /**
          * Whether the server supports searching over all messages. This requires SYNCABLE_FOLDERS
          * to be true, otherwise it should be ignored.
          */
-        public static final int SERVER_SEARCH = 0x0010;
+        public static final int SERVER_SEARCH = 0x0020;
         /**
          * Whether the server supports constraining search to a single folder. Requires
          * SYNCABLE_FOLDERS, otherwise it should be ignored.
          */
-        public static final int FOLDER_SERVER_SEARCH = 0x0020;
+        public static final int FOLDER_SERVER_SEARCH = 0x0040;
         /**
          * Whether the server sends us sanitized HTML (guaranteed to not contain malicious HTML).
          */
-        public static final int SANITIZED_HTML = 0x0040;
+        public static final int SANITIZED_HTML = 0x0080;
         /**
          * Whether the server allows synchronization of draft messages. This does NOT require
          * SYNCABLE_FOLDERS to be set.
          */
-        public static final int DRAFT_SYNCHRONIZATION = 0x0080;
+        public static final int DRAFT_SYNCHRONIZATION = 0x0100;
         /**
          * Does the server allow the user to compose mails (and reply) using addresses other than
          * their account name? For instance, GMail allows users to set FROM addresses that are
@@ -208,54 +212,54 @@
          * FROM: address like user@android.com. If the user has enabled multiple FROM address, he
          * can compose (and reply) using either address.
          */
-        public static final int MULTIPLE_FROM_ADDRESSES = 0x0100;
+        public static final int MULTIPLE_FROM_ADDRESSES = 0x0200;
         /**
          * Whether the server allows the original message to be included in the reply by setting a
          * flag on the reply. If we can avoid including the entire previous message, we save on
          * bandwidth (replies are shorter).
          */
-        public static final int SMART_REPLY = 0x0200;
+        public static final int SMART_REPLY = 0x0400;
         /**
          * Does this account support searching locally, on the device? This requires the backend
          * storage to support a mechanism for searching.
          */
-        public static final int LOCAL_SEARCH = 0x0400;
+        public static final int LOCAL_SEARCH = 0x0800;
         /**
          * Whether the server supports a notion of threaded conversations: where replies to messages
          * are tagged to keep conversations grouped. This could be full threading (each message
          * lists its parent) or conversation-level threading (each message lists one conversation
          * which it belongs to)
          */
-        public static final int THREADED_CONVERSATIONS = 0x0800;
+        public static final int THREADED_CONVERSATIONS = 0x1000;
         /**
          * Whether the server supports allowing a conversation to be in multiple folders. (Or allows
          * multiple folders on a single conversation)
          */
-        public static final int MULTIPLE_FOLDERS_PER_CONV = 0x1000;
+        public static final int MULTIPLE_FOLDERS_PER_CONV = 0x2000;
         /**
          * Whether the provider supports undoing operations. If it doesn't, never show the undo bar.
          */
-        public static final int UNDO = 0x2000;
+        public static final int UNDO = 0x4000;
         /**
          * Whether the account provides help content.
          */
-        public static final int HELP_CONTENT = 0x4000;
+        public static final int HELP_CONTENT = 0x8000;
         /**
          * Whether the account provides a way to send feedback content.
          */
-        public static final int SEND_FEEDBACK = 0x8000;
+        public static final int SEND_FEEDBACK = 0x10000;
         /**
          * Whether the account provides a mechanism for marking conversations as important.
          */
-        public static final int MARK_IMPORTANT = 0x10000;
+        public static final int MARK_IMPORTANT = 0x20000;
         /**
          * Whether initial conversation queries should use a limit parameter
          */
-        public static final int INITIAL_CONVERSATION_LIMIT = 0x20000;
+        public static final int INITIAL_CONVERSATION_LIMIT = 0x40000;
         /**
          * Whether the account cannot be used for sending
          */
-        public static final int SENDING_UNAVAILABLE = 0x40000;
+        public static final int SENDING_UNAVAILABLE = 0x80000;
     }
 
     public static final class AccountColumns {
@@ -577,29 +581,35 @@
         public static final int REPORT_SPAM = 0x0020;
 
         /**
+         * For accounts that support report spam, this will indicate that this folder supports
+         * the mark not functionality.
+         */
+        public static final int MARK_NOT_SPAM = 0x0040;
+
+        /**
          * For accounts that support mute, this will indicate if a mute is performed from within
          * this folder, the action is destructive.
          */
-        public static final int DESTRUCTIVE_MUTE = 0x0040;
+        public static final int DESTRUCTIVE_MUTE = 0x0080;
 
         /**
          * Indicates that a folder supports settings (sync lookback, etc.)
          */
-        public static final int SUPPORTS_SETTINGS = 0x0080;
+        public static final int SUPPORTS_SETTINGS = 0x0100;
         /**
          * All the messages in this folder are important.
          */
-        public static final int ONLY_IMPORTANT = 0x0100;
+        public static final int ONLY_IMPORTANT = 0x0200;
         /**
          * Deletions in this folder can't be undone (could include archive if desirable)
          */
-        public static final int DELETE_ACTION_FINAL = 0x0200;
+        public static final int DELETE_ACTION_FINAL = 0x0400;
         /**
          * This folder is virtual, i.e. contains conversations potentially pulled from other
          * folders, potentially even from different accounts.  Examples might be a "starred"
          * folder, or an "unread" folder (per account or provider-wide)
          */
-        public static final int IS_VIRTUAL = 0x400;
+        public static final int IS_VIRTUAL = 0x0800;
     }
 
     public static final class FolderColumns {
@@ -938,6 +948,11 @@
          */
         public static final String REPORT_SPAM = "report_spam";
 
+        /**
+         * Report not spam operation
+         */
+        public static final String REPORT_NOT_SPAM = "report_not_spam";
+
         private ConversationOperations() {
         }
     }
diff --git a/src/com/android/mail/ui/AbstractActivityController.java b/src/com/android/mail/ui/AbstractActivityController.java
index fcb12f2..c80d945 100644
--- a/src/com/android/mail/ui/AbstractActivityController.java
+++ b/src/com/android/mail/ui/AbstractActivityController.java
@@ -77,6 +77,7 @@
 import com.android.mail.utils.LogTag;
 import com.android.mail.utils.LogUtils;
 import com.android.mail.utils.Utils;
+import com.google.common.collect.ImmutableList;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Sets;
 
@@ -649,6 +650,11 @@
             case R.id.report_spam:
                 delete(target, getAction(R.id.report_spam, target));
                 break;
+            case R.id.mark_not_spam:
+                // Currently, since spam messages are only shown in list with other spam messages,
+                // marking a message not as spam is a destructive action
+                delete(target, getAction(R.id.mark_not_spam, target));
+                break;
             case R.id.inside_conversation_unread:
                 // TODO(viki): This is strange, and potentially incorrect. READ is an int column
                 // in the provider.
@@ -1483,7 +1489,7 @@
          */
         private final int mAction;
         /** The action will act upon these conversations */
-        private final Collection<Conversation> mTarget = new ArrayList<Conversation>();
+        private final Collection<Conversation> mTarget;
         /** Whether this destructive action has already been performed */
         private boolean mCompleted;
         /** Whether this is an action on the currently selected set. */
@@ -1498,7 +1504,7 @@
          */
         public ConversationAction(int action, Collection<Conversation> target, boolean isBatch) {
             mAction = action;
-            mTarget.addAll(target);
+            mTarget = ImmutableList.copyOf(target);
             mIsSelectedSet = isBatch;
         }
 
@@ -1553,6 +1559,10 @@
                     LogUtils.d(LOG_TAG, "Reporting spam");
                     mConversationListCursor.reportSpam(mContext, mTarget);
                     break;
+                case R.id.mark_not_spam:
+                    LogUtils.d(LOG_TAG, "Marking not spam");
+                    mConversationListCursor.reportNotSpam(mContext, mTarget);
+                    break;
                 case R.id.remove_star:
                     LogUtils.d(LOG_TAG, "Removing star");
                     // Star removal is destructive in the Starred folder.
@@ -1980,7 +1990,7 @@
      * to be animated away from the current folder.
      */
     private class FolderDestruction implements DestructiveAction {
-        private final Collection<Conversation> mTarget = new ArrayList<Conversation>();
+        private final Collection<Conversation> mTarget;
         private final ArrayList<Folder> mFolderList = new ArrayList<Folder>();
         private final boolean mIsDestructive;
         /** Whether this destructive action has already been performed */
@@ -1993,7 +2003,7 @@
          */
         private FolderDestruction(final Collection<Conversation> target,
                 final Collection<Folder> folders, boolean isDestructive, boolean isBatch) {
-            mTarget.addAll(target);
+            mTarget = ImmutableList.copyOf(target);
             mFolderList.addAll(folders);
             mIsDestructive = isDestructive;
             mIsSelectedSet = isBatch;
diff --git a/src/com/android/mail/ui/ConversationViewFragment.java b/src/com/android/mail/ui/ConversationViewFragment.java
index faea9e0..6a37e14 100644
--- a/src/com/android/mail/ui/ConversationViewFragment.java
+++ b/src/com/android/mail/ui/ConversationViewFragment.java
@@ -312,6 +312,10 @@
                 mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null
                         && mFolder.supportsCapability(FolderCapabilities.REPORT_SPAM)
                         && !mConversation.spam);
+        Utils.setMenuItemVisibility(menu, R.id.mark_not_spam,
+                mAccount.supportsCapability(AccountCapabilities.REPORT_SPAM) && mFolder != null
+                        && mFolder.supportsCapability(FolderCapabilities.MARK_NOT_SPAM)
+                        && mConversation.spam);
         Utils.setMenuItemVisibility(
                 menu,
                 R.id.mute,