"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() {
diff --git a/src/com/android/contacts/detail/ContactLoaderFragment.java b/src/com/android/contacts/detail/ContactLoaderFragment.java
index 034a8cc..daa6012 100644
--- a/src/com/android/contacts/detail/ContactLoaderFragment.java
+++ b/src/com/android/contacts/detail/ContactLoaderFragment.java
@@ -171,7 +171,7 @@
         public Loader<ContactLoader.Result> onCreateLoader(int id, Bundle args) {
             Uri lookupUri = args.getParcelable(LOADER_ARG_CONTACT_URI);
             return new ContactLoader(mContext, lookupUri, true /* loadGroupMetaData */,
-                    true /* loadStreamItems */);
+                    true /* loadStreamItems */, false /* load invitable account types */);
         }
 
         @Override
diff --git a/src/com/android/contacts/model/AccountType.java b/src/com/android/contacts/model/AccountType.java
index 70aa430..5de4340 100644
--- a/src/com/android/contacts/model/AccountType.java
+++ b/src/com/android/contacts/model/AccountType.java
@@ -33,6 +33,7 @@
 import android.provider.ContactsContract.RawContacts;
 import android.widget.EditText;
 
+import java.text.Collator;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.Comparator;
@@ -120,7 +121,7 @@
      */
     public CharSequence getInviteContactActionLabel(Context context) {
         return getResourceText(context, summaryResPackageName, getInviteContactActionResId(context),
-                null);
+                "");
     }
 
     /**
@@ -342,4 +343,28 @@
         public CharSequence inflateUsing(Context context, Cursor cursor);
         public CharSequence inflateUsing(Context context, ContentValues values);
     }
+
+    /**
+     * Compare two {@link AccountType} by their {@link AccountType#getDisplayLabel} with the
+     * current locale.
+     */
+    public static class DisplayLabelComparator implements Comparator<AccountType> {
+        private final Context mContext;
+        /** {@link Comparator} for the current locale. */
+        private final Collator mCollator = Collator.getInstance();
+
+        public DisplayLabelComparator(Context context) {
+            mContext = context;
+        }
+
+        private String getDisplayLabel(AccountType type) {
+            CharSequence label = type.getDisplayLabel(mContext);
+            return (label == null) ? "" : label.toString();
+        }
+
+        @Override
+        public int compare(AccountType lhs, AccountType rhs) {
+            return mCollator.compare(getDisplayLabel(lhs), getDisplayLabel(rhs));
+        }
+    }
 }
diff --git a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
index d59aebd..3d7881b 100644
--- a/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
+++ b/src/com/android/contacts/socialwidget/SocialWidgetProvider.java
@@ -111,7 +111,8 @@
             // Not yet set-up (this can happen while the Configuration activity is visible)
             return;
         }
-        final ContactLoader contactLoader = new ContactLoader(context, contactUri, false, true);
+        final ContactLoader contactLoader = new ContactLoader(context, contactUri, false, true,
+                false);
         contactLoader.registerListener(0,
                 new ContactLoader.OnLoadCompleteListener<ContactLoader.Result>() {
                     @Override