Merge "Cleaner implementation; use resource" into jb-ub-mail
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 00223b5..484d6a2 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -693,4 +693,9 @@
 
     <!-- Camera format string for new image files. Passed to java.text.SimpleDateFormat. -->
     <string name="image_file_name_format" translatable="false">"'IMG'_yyyyMMdd_HHmmss"</string>
+
+    <!--  The move message / change labels action can't be taken because the selected messages
+        come from different accounts -->
+    <string name="cant_move_or_change_labels">Can\'t move because selection contains multiple
+        accounts.</string>
 </resources>
diff --git a/src/com/android/mail/browse/SelectedConversationsActionMenu.java b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
index b3a9488..13fd7cb 100644
--- a/src/com/android/mail/browse/SelectedConversationsActionMenu.java
+++ b/src/com/android/mail/browse/SelectedConversationsActionMenu.java
@@ -20,24 +20,27 @@
 import android.app.AlertDialog;
 import android.content.Context;
 import android.content.DialogInterface;
+import android.net.Uri;
 import android.view.ActionMode;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
+import android.widget.Toast;
 
 import com.android.mail.R;
 import com.android.mail.providers.Account;
 import com.android.mail.providers.Conversation;
 import com.android.mail.providers.Folder;
+import com.android.mail.providers.MailAppProvider;
 import com.android.mail.providers.Settings;
 import com.android.mail.providers.UIProvider;
 import com.android.mail.providers.UIProvider.ConversationColumns;
 import com.android.mail.providers.UIProvider.FolderCapabilities;
 import com.android.mail.ui.AbstractActivityController;
-import com.android.mail.ui.DestructiveAction;
 import com.android.mail.ui.AnimatedAdapter;
 import com.android.mail.ui.ConversationSelectionSet;
 import com.android.mail.ui.ConversationSetObserver;
+import com.android.mail.ui.DestructiveAction;
 import com.android.mail.ui.FoldersSelectionDialog;
 import com.android.mail.ui.RestrictedActivity;
 import com.android.mail.ui.SwipeableListView;
@@ -158,8 +161,31 @@
                 }
                 break;
             case R.id.change_folder:
-                new FoldersSelectionDialog(mContext, mAccount, mController, mSelectionSet.values(),
-                        true).show();
+                boolean cantMove = false;
+                Account acct = mAccount;
+                // Special handling for virtual folders
+                if (mFolder.supportsCapability(FolderCapabilities.IS_VIRTUAL)) {
+                    Uri accountUri = null;
+                    for (Conversation conv: mSelectionSet.values()) {
+                        if (accountUri == null) {
+                            accountUri = conv.accountUri;
+                        } else if (!accountUri.equals(conv.accountUri)) {
+                            // Tell the user why we can't do this
+                            Toast.makeText(mContext, R.string.cant_move_or_change_labels,
+                                    Toast.LENGTH_LONG).show();
+                            cantMove = true;
+                            break;
+                        }
+                    }
+                    if (!cantMove) {
+                        // Get the actual account here, so that we display its folders in the dialog
+                        acct = MailAppProvider.getAccountFromAccountUri(accountUri);
+                    }
+                }
+                if (!cantMove) {
+                    new FoldersSelectionDialog(mContext, acct, mController,
+                            mSelectionSet.values(), true).show();
+                }
                 break;
             case R.id.mark_important:
                 markConversationsImportant(true);
diff --git a/src/com/android/mail/providers/Conversation.java b/src/com/android/mail/providers/Conversation.java
index cba2ac7..1bb7d1c 100644
--- a/src/com/android/mail/providers/Conversation.java
+++ b/src/com/android/mail/providers/Conversation.java
@@ -16,14 +16,14 @@
 
 package com.android.mail.providers;
 
-import com.google.common.collect.ImmutableList;
-
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Parcel;
 import android.os.Parcelable;
 import android.text.TextUtils;
 
+import com.google.common.collect.ImmutableList;
+
 import java.util.Collection;
 import java.util.Collections;
 
@@ -51,6 +51,7 @@
     public boolean spam;
     public boolean muted;
     public int color;
+    public Uri accountUri;
 
     // Used within the UI to indicate the adapter position of this conversation
     public transient int position;
@@ -96,6 +97,7 @@
         dest.writeInt(spam ? 1 : 0);
         dest.writeInt(muted ? 1 : 0);
         dest.writeInt(color);
+        dest.writeParcelable(accountUri, 0);
     }
 
     private Conversation(Parcel in) {
@@ -120,6 +122,7 @@
         spam = in.readInt() != 0;
         muted = in.readInt() != 0;
         color = in.readInt();
+        accountUri = in.readParcelable(null);
         position = NO_POSITION;
         localDeleteOnUpdate = false;
     }
@@ -174,6 +177,8 @@
             spam = cursor.getInt(UIProvider.CONVERSATION_IS_SPAM_COLUMN) != 0;
             muted = cursor.getInt(UIProvider.CONVERSATION_MUTED_COLUMN) != 0;
             color = cursor.getInt(UIProvider.CONVERSATION_COLOR_COLUMN);
+            String account = cursor.getString(UIProvider.CONVERSATION_ACCOUNT_URI_COLUMN);
+            accountUri = !TextUtils.isEmpty(account) ? Uri.parse(account) : null;
             position = NO_POSITION;
             localDeleteOnUpdate = false;
         }
@@ -186,7 +191,7 @@
             String snippet, boolean hasAttachment, Uri messageListUri, String senders,
             int numMessages, int numDrafts, int sendingState, int priority, boolean read,
             boolean starred, String folderList, String rawFolders, int convFlags,
-            int personalLevel, boolean spam, boolean muted) {
+            int personalLevel, boolean spam, boolean muted, Uri accountUri) {
 
         final Conversation conversation = new Conversation();
 
@@ -211,6 +216,7 @@
         conversation.spam = spam;
         conversation.muted = muted;
         conversation.color = 0;
+        conversation.accountUri = accountUri;
         return conversation;
     }
 
diff --git a/src/com/android/mail/providers/MailAppProvider.java b/src/com/android/mail/providers/MailAppProvider.java
index f2d022f..d2a079b 100644
--- a/src/com/android/mail/providers/MailAppProvider.java
+++ b/src/com/android/mail/providers/MailAppProvider.java
@@ -16,6 +16,7 @@
 
 package com.android.mail.providers;
 
+import android.app.Activity;
 import android.content.ContentProvider;
 import android.content.ContentResolver;
 import android.content.ContentValues;
@@ -459,6 +460,19 @@
         return mSharedPrefs;
     }
 
+    static public Account getAccountFromAccountUri(Uri accountUri) {
+        MailAppProvider provider = getInstance();
+        if (provider != null && provider.mAccountsFullyLoaded) {
+            synchronized(provider.mAccountCache) {
+                AccountCacheEntry entry = provider.mAccountCache.get(accountUri);
+                if (entry != null) {
+                    return entry.mAccount;
+                }
+            }
+        }
+        return null;
+    }
+
     @Override
     public void onLoadComplete(Loader<Cursor> loader, Cursor data) {
         if (data == null) {
diff --git a/src/com/android/mail/providers/UIProvider.java b/src/com/android/mail/providers/UIProvider.java
index 228530e..d5ef631 100644
--- a/src/com/android/mail/providers/UIProvider.java
+++ b/src/com/android/mail/providers/UIProvider.java
@@ -572,6 +572,12 @@
          * Deletions in this folder can't be undone (could include archive if desirable)
          */
         public static final int DELETE_ACTION_FINAL = 0x0200;
+        /**
+         * 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 class FolderColumns {
@@ -679,7 +685,8 @@
         ConversationColumns.PERSONAL_LEVEL,
         ConversationColumns.SPAM,
         ConversationColumns.MUTED,
-        ConversationColumns.COLOR
+        ConversationColumns.COLOR,
+        ConversationColumns.ACCOUNT_URI
     };
 
     // These column indexes only work when the caller uses the
@@ -705,6 +712,7 @@
     public static final int CONVERSATION_IS_SPAM_COLUMN = 18;
     public static final int CONVERSATION_MUTED_COLUMN = 19;
     public static final int CONVERSATION_COLOR_COLUMN = 20;
+    public static final int CONVERSATION_ACCOUNT_URI_COLUMN = 21;
 
     public static final class ConversationSendingState {
         public static final int OTHER = 0;
@@ -833,6 +841,11 @@
          */
         public static final String COLOR = "color";
 
+        /**
+         * This String column contains the Uri for this conversation's account
+         */
+        public static final String ACCOUNT_URI = "accountUri";
+
         private ConversationColumns() {
         }
     }