"Invite" part 2

- ContactLoader now loads invitable account types, which are account types
  that supports the invite feature and don't contribute to the loaded
  contact.
- Add AccountType.DisplayLabelComparator

Bug 5061956

Change-Id: Icadffac9e5e3b739e70f59d709cfdd67909c9a41
diff --git a/src/com/android/contacts/ContactLoader.java b/src/com/android/contacts/ContactLoader.java
index fa0ffb2..4078598 100644
--- a/src/com/android/contacts/ContactLoader.java
+++ b/src/com/android/contacts/ContactLoader.java
@@ -16,9 +16,12 @@
 
 package com.android.contacts;
 
+import com.android.contacts.model.AccountType;
+import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.util.DataStatus;
 import com.android.contacts.util.StreamItemEntry;
 import com.android.contacts.util.StreamItemPhotoEntry;
+import com.google.android.collect.Lists;
 import com.google.common.annotations.VisibleForTesting;
 
 import android.content.ContentResolver;
@@ -44,23 +47,20 @@
 import android.provider.ContactsContract.DisplayNameSources;
 import android.provider.ContactsContract.Groups;
 import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.StreamItems;
 import android.provider.ContactsContract.StreamItemPhotos;
+import android.provider.ContactsContract.StreamItems;
 import android.text.TextUtils;
 import android.util.Log;
 
 import java.io.ByteArrayOutputStream;
 import java.io.FileInputStream;
-import java.io.FileNotFoundException;
 import java.io.IOException;
 import java.io.InputStream;
 import java.util.ArrayList;
-import java.util.Arrays;
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
-import java.util.Set;
 
 /**
  * Loads a single Contact and all it constituent RawContacts.
@@ -71,6 +71,7 @@
     private Uri mLookupUri;
     private boolean mLoadGroupMetaData;
     private boolean mLoadStreamItems;
+    private final boolean mLoadInvitableAccountTypes;
     private Result mContact;
     private ForceLoadContentObserver mObserver;
     private boolean mDestroyed;
@@ -113,6 +114,7 @@
         private final ArrayList<Entity> mEntities;
         private ArrayList<StreamItemEntry> mStreamItems;
         private final HashMap<Long, DataStatus> mStatuses;
+        private final ArrayList<String> mInvitableAccountTypes;
 
         private String mDirectoryDisplayName;
         private String mDirectoryType;
@@ -147,6 +149,7 @@
             mPhoneticName = null;
             mStarred = false;
             mPresence = null;
+            mInvitableAccountTypes = null;
         }
 
         /**
@@ -173,6 +176,7 @@
             mPhoneticName = phoneticName;
             mStarred = starred;
             mPresence = presence;
+            mInvitableAccountTypes = Lists.newArrayList();
         }
 
         private Result(Result from) {
@@ -193,6 +197,7 @@
             mEntities = from.mEntities;
             mStreamItems = from.mStreamItems;
             mStatuses = from.mStatuses;
+            mInvitableAccountTypes = from.mInvitableAccountTypes;
 
             mDirectoryDisplayName = from.mDirectoryDisplayName;
             mDirectoryType = from.mDirectoryType;
@@ -279,6 +284,10 @@
             return mPresence;
         }
 
+        public ArrayList<String> getInvitableAccontTypes() {
+            return mInvitableAccountTypes;
+        }
+
         public ArrayList<Entity> getEntities() {
             return mEntities;
         }
@@ -568,6 +577,9 @@
                         loadStreamItems(result);
                     }
                     loadPhotoBinaryData(result);
+                    if (mLoadInvitableAccountTypes) {
+                        loadInvitableAccountTypes(result);
+                    }
                 }
                 return result;
             } catch (Exception e) {
@@ -718,6 +730,27 @@
             }
         }
 
+        private void loadInvitableAccountTypes(Result contactData) {
+            Map<String, AccountType> allInvitables =
+                    AccountTypeManager.getInstance(getContext()).getInvitableAccountTypes();
+            if (allInvitables.isEmpty()) {
+                return;
+            }
+
+            HashMap<String, AccountType> result = new HashMap<String, AccountType>(allInvitables);
+
+            // Remove the ones that already has a raw contact in the current contact
+            for (Entity entity : contactData.getEntities()) {
+                final String type = entity.getEntityValues().getAsString(RawContacts.ACCOUNT_TYPE);
+                if (!TextUtils.isEmpty(type)) {
+                    result.remove(type);
+                }
+            }
+
+            // Set to mInvitableAccountTypes
+            contactData.mInvitableAccountTypes.addAll(result.keySet());
+        }
+
         /**
          * Extracts Contact level columns from the cursor.
          */
@@ -1058,15 +1091,16 @@
     }
 
     public ContactLoader(Context context, Uri lookupUri) {
-        this(context, lookupUri, false, false);
+        this(context, lookupUri, false, false, false);
     }
 
     public ContactLoader(Context context, Uri lookupUri, boolean loadGroupMetaData,
-            boolean loadStreamItems) {
+            boolean loadStreamItems, boolean loadInvitableAccountTypes) {
         super(context);
         mLookupUri = lookupUri;
         mLoadGroupMetaData = loadGroupMetaData;
         mLoadStreamItems = loadStreamItems;
+        mLoadInvitableAccountTypes = loadInvitableAccountTypes;
     }
 
     public Uri getLookupUri() {