Adds secondary actions for hangouts and g plus

bug: 17374362
Change-Id: Ib4535669737d4132dede07e39aa61e29cc94e5b5
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index e5574d9..268b151 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -191,10 +191,12 @@
 
     private static final String MIMETYPE_GPLUS_PROFILE =
             "vnd.android.cursor.item/vnd.googleplus.profile";
-    private static final String INTENT_DATA_GPLUS_PROFILE_ADD_TO_CIRCLE = "Add to circle";
+    private static final String GPLUS_PROFILE_DATA_5_ADD_TO_CIRCLE = "addtocircle";
+    private static final String GPLUS_PROFILE_DATA_5_VIEW_PROFILE = "view";
     private static final String MIMETYPE_HANGOUTS =
             "vnd.android.cursor.item/vnd.googleplus.profile.comm";
-    private static final String INTENT_DATA_HANGOUTS_VIDEO = "Start video call";
+    private static final String HANGOUTS_DATA_5_VIDEO = "hangout";
+    private static final String HANGOUTS_DATA_5_MESSAGE = "conversation";
     private static final String CALL_ORIGIN_QUICK_CONTACTS_ACTIVITY =
             "com.android.contacts.quickcontact.QuickContactActivity";
 
@@ -1241,9 +1243,11 @@
      * additional dependencies on unsafe things (like the Activity).
      *
      * @param dataItem The {@link DataItem} to convert.
+     * @param secondDataItem A second {@link DataItem} to help build a full entry for some
+     *  mimetypes
      * @return The {@link ExpandingEntryCardView.Entry}, or null if no visual elements are present.
      */
-    private static Entry dataItemToEntry(DataItem dataItem,
+    private static Entry dataItemToEntry(DataItem dataItem, DataItem secondDataItem,
             Context context, Contact contactData,
             final MutableString aboutCardName) {
         Drawable icon = null;
@@ -1493,26 +1497,57 @@
             if (intent != null) {
                 final String mimetype = intent.getType();
 
-                // Attempt to use known icons for known 3p types. Otherwise default to ResolveCache
+                // Build advanced entry for known 3p types. Otherwise default to ResolveCache icon.
                 switch (mimetype) {
                     case MIMETYPE_GPLUS_PROFILE:
-                        if (INTENT_DATA_GPLUS_PROFILE_ADD_TO_CIRCLE.equals(
-                                intent.getDataString())) {
-                            icon = res.getDrawable(
-                                    R.drawable.ic_add_to_circles_black_24);
-                            iconResourceId = R.drawable.ic_add_to_circles_black_24;
-                        } else {
+                        // If a secondDataItem is available, use it to build an entry with
+                        // alternate actions
+                        if (secondDataItem != null) {
                             icon = res.getDrawable(R.drawable.ic_google_plus_24dp);
-                            iconResourceId = R.drawable.ic_google_plus_24dp;
+                            alternateIcon = res.getDrawable(R.drawable.ic_add_to_circles_black_24);
+                            final GPlusOrHangoutsDataItemModel itemModel =
+                                    new GPlusOrHangoutsDataItemModel(intent, alternateIntent,
+                                            dataItem, secondDataItem, alternateContentDescription,
+                                            header, text, context);
+
+                            populateGPlusOrHangoutsDataItemModel(bundle);
+                            intent = bundle.intent;
+                            alternateIntent = bundle.alternateIntent;
+                            alternateContentDescription = bundle.alternateContentDescription;
+                            header = bundle.header;
+                            text = bundle.text;
+                        } else {
+                            if (GPLUS_PROFILE_DATA_5_ADD_TO_CIRCLE.equals(
+                                    intent.getDataString())) {
+                                icon = res.getDrawable(R.drawable.ic_add_to_circles_black_24);
+                            } else {
+                                icon = res.getDrawable(R.drawable.ic_google_plus_24dp);
+                            }
                         }
                         break;
                     case MIMETYPE_HANGOUTS:
-                        if (INTENT_DATA_HANGOUTS_VIDEO.equals(intent.getDataString())) {
-                            icon = res.getDrawable(R.drawable.ic_hangout_video_24dp);
-                            iconResourceId = R.drawable.ic_hangout_video_24dp;
-                        } else {
+                        // If a secondDataItem is available, use it to build an entry with
+                        // alternate actions
+                        if (secondDataItem != null) {
                             icon = res.getDrawable(R.drawable.ic_hangout_24dp);
-                            iconResourceId = R.drawable.ic_hangout_24dp;
+                            alternateIcon = res.getDrawable(R.drawable.ic_hangout_video_24dp);
+                            final GPlusOrHangoutsDataItemModel bundle =
+                                    new GPlusOrHangoutsDataItemModel(intent, alternateIntent,
+                                            dataItem, secondDataItem, alternateContentDescription,
+                                            header, text, context);
+
+                            populateGPlusOrHangoutsDataItemModel(bundle);
+                            intent = bundle.intent;
+                            alternateIntent = bundle.alternateIntent;
+                            alternateContentDescription = bundle.alternateContentDescription;
+                            header = bundle.header;
+                            text = bundle.text;
+                        } else {
+                            if (HANGOUTS_DATA_5_VIDEO.equals(intent.getDataString())) {
+                                icon = res.getDrawable(R.drawable.ic_hangout_video_24dp);
+                            } else {
+                                icon = res.getDrawable(R.drawable.ic_hangout_24dp);
+                            }
                         }
                         break;
                     default:
@@ -1566,16 +1601,124 @@
 
     private List<Entry> dataItemsToEntries(List<DataItem> dataItems,
             MutableString aboutCardTitleOut) {
+        // Hangouts and G+ use two data items to create one entry.
+        if (dataItems.get(0).getMimeType().equals(MIMETYPE_GPLUS_PROFILE) ||
+                dataItems.get(0).getMimeType().equals(MIMETYPE_HANGOUTS)) {
+            return gPlusOrHangoutsDataItemsToEntries(dataItems);
+        } else {
+            final List<Entry> entries = new ArrayList<>();
+            for (DataItem dataItem : dataItems) {
+                final Entry entry = dataItemToEntry(dataItem, /* secondDataItem = */ null,
+                        this, mContactData, aboutCardTitleOut);
+                if (entry != null) {
+                    entries.add(entry);
+                }
+            }
+            return entries;
+        }
+    }
+
+    /**
+     * G+ and Hangout entries are unique in that a single ExpandingEntryCardView.Entry consists
+     * of two data items. This method attempts to build each entry using the two data items if
+     * they are available. If there are more or less than two data items, a fall back is used
+     * and each data item gets its own entry.
+     */
+    private List<Entry> gPlusOrHangoutsDataItemsToEntries(List<DataItem> dataItems) {
         final List<Entry> entries = new ArrayList<>();
+        final Map<Long, List<DataItem>> buckets = new HashMap<>();
+        // Put the data items into buckets based on the raw contact id
         for (DataItem dataItem : dataItems) {
-            final Entry entry = dataItemToEntry(dataItem, this, mContactData, aboutCardTitleOut);
-            if (entry != null) {
-                entries.add(entry);
+            List<DataItem> bucket = buckets.get(dataItem.getRawContactId());
+            if (bucket == null) {
+                bucket = new ArrayList<>();
+                buckets.put(dataItem.getRawContactId(), bucket);
+            }
+            bucket.add(dataItem);
+        }
+
+        // Use the buckets to build entries. If a bucket contains two data items, build the special
+        // entry, otherwise fall back to the normal entry.
+        for (List<DataItem> bucket : buckets.values()) {
+            if (bucket.size() == 2) {
+                // Use the pair to build an entry
+                final Entry entry = dataItemToEntry(bucket.get(0),
+                        /* secondDataItem = */ bucket.get(1), this, mContactData,
+                        /* aboutCardName = */ null);
+                if (entry != null) {
+                    entries.add(entry);
+                }
+            } else {
+                for (DataItem dataItem : bucket) {
+                    final Entry entry = dataItemToEntry(dataItem, /* secondDataItem = */ null,
+                            this, mContactData, /* aboutCardName = */ null);
+                    if (entry != null) {
+                        entries.add(entry);
+                    }
+                }
             }
         }
         return entries;
     }
 
+    /**
+     * Used for statically passing around G+ or Hangouts data items and entry fields to
+     * populateGPlusOrHangoutsDataItemModel.
+     */
+    private static final class GPlusOrHangoutsDataItemModel {
+        public Intent intent;
+        public Intent alternateIntent;
+        public DataItem dataItem;
+        public DataItem secondDataItem;
+        public StringBuilder alternateContentDescription;
+        public String header;
+        public String text;
+        public Context context;
+
+        public GPlusOrHangoutsDataItemModel(Intent intent, Intent alternateIntent, DataItem dataItem,
+                DataItem secondDataItem, StringBuilder alternateContentDescription, String header,
+                String text, Context context) {
+            this.intent = intent;
+            this.alternateIntent = alternateIntent;
+            this.dataItem = dataItem;
+            this.secondDataItem = secondDataItem;
+            this.alternateContentDescription = alternateContentDescription;
+            this.header = header;
+            this.text = text;
+            this.context = context;
+        }
+    }
+
+    private static void populateGPlusOrHangoutsDataItemModel(
+            GPlusOrHangoutsDataItemModel dataModel) {
+        final Intent secondIntent = new Intent(Intent.ACTION_VIEW);
+        secondIntent.setDataAndType(ContentUris.withAppendedId(Data.CONTENT_URI,
+                dataModel.secondDataItem.getId()), dataModel.secondDataItem.getMimeType());
+        // There is no guarantee the order the data items come in. Second
+        // data item does not necessarily mean it's the alternate.
+        // Hangouts video and Add to circles should be alternate. Swap if needed
+        if (HANGOUTS_DATA_5_VIDEO.equals(
+                dataModel.dataItem.getContentValues().getAsString(Data.DATA5)) ||
+                GPLUS_PROFILE_DATA_5_ADD_TO_CIRCLE.equals(
+                        dataModel.dataItem.getContentValues().getAsString(Data.DATA5))) {
+            dataModel.alternateIntent = dataModel.intent;
+            dataModel.alternateContentDescription = new StringBuilder(dataModel.header);
+
+            dataModel.intent = secondIntent;
+            dataModel.header = dataModel.secondDataItem.buildDataStringForDisplay(dataModel.context,
+                    dataModel.secondDataItem.getDataKind());
+            dataModel.text = dataModel.secondDataItem.getDataKind().typeColumn;
+        } else if (HANGOUTS_DATA_5_MESSAGE.equals(
+                dataModel.dataItem.getContentValues().getAsString(Data.DATA5)) ||
+                GPLUS_PROFILE_DATA_5_VIEW_PROFILE.equals(
+                        dataModel.dataItem.getContentValues().getAsString(Data.DATA5))) {
+            dataModel.alternateIntent = secondIntent;
+            dataModel.alternateContentDescription = new StringBuilder(
+                    dataModel.secondDataItem.buildDataStringForDisplay(dataModel.context,
+                            dataModel.secondDataItem.getDataKind()));
+        }
+    }
+
     private static String getIntentResolveLabel(Intent intent, Context context) {
         final List<ResolveInfo> matches = context.getPackageManager().queryIntentActivities(intent,
                 PackageManager.MATCH_DEFAULT_ONLY);
@@ -1822,18 +1965,15 @@
 
         @Override
         public Loader<List<ContactInteraction>> onCreateLoader(int id, Bundle args) {
-            Log.v(TAG, "onCreateLoader");
             Loader<List<ContactInteraction>> loader = null;
             switch (id) {
                 case LOADER_SMS_ID:
-                    Log.v(TAG, "LOADER_SMS_ID");
                     loader = new SmsInteractionsLoader(
                             QuickContactActivity.this,
                             args.getStringArray(KEY_LOADER_EXTRA_PHONES),
                             MAX_SMS_RETRIEVE);
                     break;
                 case LOADER_CALENDAR_ID:
-                    Log.v(TAG, "LOADER_CALENDAR_ID");
                     final String[] emailsArray = args.getStringArray(KEY_LOADER_EXTRA_EMAILS);
                     List<String> emailsList = null;
                     if (emailsArray != null) {
@@ -1848,7 +1988,6 @@
                             PAST_MILLISECOND_TO_SEARCH_LOCAL_CALENDAR);
                     break;
                 case LOADER_CALL_LOG_ID:
-                    Log.v(TAG, "LOADER_CALL_LOG_ID");
                     loader = new CallLogInteractionsLoader(
                             QuickContactActivity.this,
                             args.getStringArray(KEY_LOADER_EXTRA_PHONES),