Add alternative video call to QC action

Force the clickable icons in the entry card to be 48dp.
Add the reachability DataItem to the PhoneDataItem so we
have all the necessary data for creating an Entry.
Add a boolean for whether we should color the "third icon", we don't
want to tint it when using the icon from a third party app.
Since we're adding the reachable action to the phone number row,
skip creating the custom row.

Consider cleaning up the Entry constructor (maybe in 2.1), 5 of the 6
uses pass in a lot of null/default values. Possibly use a Builder
or removing the finals from the fields.

Test: Manually verified phone numbers which are reachable on Duo, have
the corresponding action and the custom row isn't visible.
https://screenshot.googleplex.com/vqoCjGwCpYd

Bug: 36874501
Change-Id: Ie3d0fb6ca6491e0fd3d890d47980cc845b9241db
diff --git a/res/layout/expanding_entry_card_item.xml b/res/layout/expanding_entry_card_item.xml
index 99f9174..dbbeee5 100644
--- a/res/layout/expanding_entry_card_item.xml
+++ b/res/layout/expanding_entry_card_item.xml
@@ -88,8 +88,8 @@
 
      <ImageView
          android:id="@+id/third_icon"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
+         android:layout_width="@dimen/default_clickable_icon_size"
+         android:layout_height="@dimen/default_clickable_icon_size"
          android:layout_alignParentTop="true"
          android:layout_toStartOf="@+id/icon_alternate"
          android:layout_alignWithParentIfMissing="true"
@@ -102,8 +102,8 @@
 
      <ImageView
          android:id="@+id/icon_alternate"
-         android:layout_width="wrap_content"
-         android:layout_height="wrap_content"
+         android:layout_width="@dimen/default_clickable_icon_size"
+         android:layout_height="@dimen/default_clickable_icon_size"
          android:layout_alignParentEnd="true"
          android:layout_alignParentTop="true"
          android:visibility="gone"
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index e9fe2ad..c14d396 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -14,6 +14,8 @@
      limitations under the License.
 -->
 <resources>
+    <!-- Size of clickable icons that are GAR-4 -->
+    <dimen name="default_clickable_icon_size">48dp</dimen>
     <!-- Copied from java/com/google/android/assets/launchscreens/res/values-port-v21/dimens.xml -->
     <!-- Values adjusted for nav bar size due to windowDrawsSystemBarBackgrounds -->
     <dimen name="launchscreens_product_logo_bottom">64dp</dimen>
diff --git a/src/com/android/contacts/model/dataitem/PhoneDataItem.java b/src/com/android/contacts/model/dataitem/PhoneDataItem.java
index 8d6cf8e..6355157 100644
--- a/src/com/android/contacts/model/dataitem/PhoneDataItem.java
+++ b/src/com/android/contacts/model/dataitem/PhoneDataItem.java
@@ -31,6 +31,11 @@
 
     public static final String KEY_FORMATTED_PHONE_NUMBER = "formattedPhoneNumber";
 
+    private boolean mTachyonReachable;
+    // Stores the custom reachable data item to provide extra data to the Entry created from this
+    // PhoneDataItem.
+    private DataItem mReachableDataItem;
+
     /* package */ PhoneDataItem(ContentValues values) {
         super(values);
     }
@@ -54,6 +59,22 @@
         return getContentValues().getAsString(Phone.LABEL);
     }
 
+    public void setTachyonReachable(boolean tachyonReachable) {
+        mTachyonReachable = tachyonReachable;
+    }
+
+    public boolean isTachyonReachable() {
+        return mTachyonReachable;
+    }
+
+    public DataItem getReachableDataItem() {
+        return mReachableDataItem;
+    }
+
+    public void setReachableDataItem(DataItem reachableDataItem) {
+        mReachableDataItem = reachableDataItem;
+    }
+
     public void computeFormattedPhoneNumber(String defaultCountryIso) {
         final String phoneNumber = getNumber();
         if (phoneNumber != null) {
diff --git a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
index 49da4c8..b0f7800 100644
--- a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
+++ b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
@@ -117,6 +117,7 @@
         private final int mIconResourceId;
         private final int mThirdAction;
         private final Bundle mThirdExtras;
+        private final boolean mShouldApplyThirdIconColor;
 
         public Entry(int id, Drawable mainIcon, String header, String subHeader,
                 Drawable subHeaderIcon, String text, Drawable textIcon,
@@ -125,7 +126,7 @@
                 Spannable alternateContentDescription, boolean shouldApplyColor, boolean isEditable,
                 EntryContextMenuInfo entryContextMenuInfo, Drawable thirdIcon, Intent thirdIntent,
                 String thirdContentDescription, int thirdAction, Bundle thirdExtras,
-                int iconResourceId) {
+                boolean shouldApplyThirdIconColor, int iconResourceId) {
             mId = id;
             mIcon = mainIcon;
             mHeader = header;
@@ -146,6 +147,7 @@
             mThirdContentDescription = thirdContentDescription;
             mThirdAction = thirdAction;
             mThirdExtras = thirdExtras;
+            mShouldApplyThirdIconColor = shouldApplyThirdIconColor;
             mIconResourceId = iconResourceId;
         }
 
@@ -232,6 +234,10 @@
         public Bundle getThirdExtras() {
             return mThirdExtras;
         }
+
+        boolean shouldApplyThirdIconColor() {
+            return mShouldApplyThirdIconColor;
+        }
     }
 
     public interface ExpandingEntryCardViewListener {
@@ -607,7 +613,7 @@
                             alternateIcon.setColorFilter(mThemeColorFilter);
                         }
                         Drawable thirdIcon = entry.getThirdIcon();
-                        if (thirdIcon != null) {
+                        if (thirdIcon != null && entry.shouldApplyThirdIconColor()) {
                             thirdIcon.mutate();
                             thirdIcon.setColorFilter(mThemeColorFilter);
                         }
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 014e1d5..f56234f 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -105,8 +105,11 @@
 import com.android.contacts.ContactsActivity;
 import com.android.contacts.ContactsUtils;
 import com.android.contacts.DynamicShortcuts;
+import com.android.contacts.Experiments;
 import com.android.contacts.NfcHandler;
 import com.android.contacts.R;
+import com.android.contacts.ShortcutIntentBuilder;
+import com.android.contacts.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
 import com.android.contacts.activities.ContactEditorActivity;
 import com.android.contacts.activities.ContactSelectionActivity;
 import com.android.contacts.activities.RequestDesiredPermissionsActivity;
@@ -126,8 +129,6 @@
 import com.android.contacts.interactions.SmsInteractionsLoader;
 import com.android.contacts.interactions.TouchPointManager;
 import com.android.contacts.lettertiles.LetterTileDrawable;
-import com.android.contacts.ShortcutIntentBuilder;
-import com.android.contacts.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
 import com.android.contacts.list.UiIntentActions;
 import com.android.contacts.logging.Logger;
 import com.android.contacts.logging.QuickContactEvent.ActionType;
@@ -175,6 +176,7 @@
 import com.android.contacts.widget.MultiShrinkScroller.MultiShrinkScrollerListener;
 import com.android.contacts.widget.QuickContactImageView;
 import com.android.contactsbind.HelpUtils;
+import com.android.contactsbind.experiments.Flags;
 
 import com.google.common.collect.Lists;
 
@@ -246,6 +248,8 @@
     @SuppressWarnings("deprecation")
     private static final String LEGACY_AUTHORITY = android.provider.Contacts.AUTHORITY;
 
+    public static final String MIMETYPE_TACHYON =
+            "vnd.android.cursor.item/com.google.android.apps.tachyon.phone";
     private static final String MIMETYPE_GPLUS_PROFILE =
             "vnd.android.cursor.item/vnd.googleplus.profile";
     private static final String GPLUS_PROFILE_DATA_5_VIEW_PROFILE = "view";
@@ -1328,6 +1332,7 @@
                     /* thirdContentDescription = */ null,
                     /* thirdAction = */ Entry.ACTION_NONE,
                     /* thirdExtras = */ null,
+                    /* shouldApplyThirdIconColor = */ true,
                     /* iconResourceId = */  0);
             List<Entry> phoneticList = new ArrayList<>();
             phoneticList.add(phoneticEntry);
@@ -1390,6 +1395,7 @@
                 /* thirdContentDescription = */ null,
                 /* thirdAction = */ Entry.ACTION_NONE,
                 /* thirdExtras = */ null,
+                /* shouldApplyThirdIconColor = */ true,
                 R.drawable.quantum_ic_phone_vd_theme_24);
 
         final List<List<Entry>> promptEntries = new ArrayList<>();
@@ -1409,6 +1415,7 @@
                     /* EntryContextMenuInfo = */ null, /* thirdIcon = */ null,
                     /* thirdIntent = */ null, /* thirdContentDescription = */ null,
                     /* thirdAction = */ Entry.ACTION_NONE, /* thirdExtras = */ null,
+                    /* shouldApplyThirdIconColor = */ true,
                     R.drawable.quantum_ic_email_vd_theme_24);
 
             promptEntries.add(new ArrayList<Entry>(1));
@@ -1443,7 +1450,6 @@
 
         final Map<String, List<DataItem>> dataItemsMap = new HashMap<>();
 
-        final ResolveCache cache = ResolveCache.getInstance(this);
         for (RawContact rawContact : data.getRawContacts()) {
             for (DataItem dataItem : rawContact.getDataItems()) {
                 dataItem.setRawContactId(rawContact.getId());
@@ -1472,6 +1478,7 @@
             }
         }
         Trace.endSection();
+        bindReachability(dataItemsMap);
 
         Trace.beginSection("sort within mimetypes");
         /*
@@ -1529,6 +1536,28 @@
     }
 
     /**
+     * Bind the custom data items to each {@link PhoneDataItem} that is Tachyon reachable, the data
+     * will be needed when creating the {@link Entry} for the {@link PhoneDataItem}.
+     */
+    private void bindReachability(Map<String, List<DataItem>> dataItemsMap) {
+        final List<DataItem> phoneItems = dataItemsMap.get(Phone.CONTENT_ITEM_TYPE);
+        final List<DataItem> tachyonItems = dataItemsMap.get(MIMETYPE_TACHYON);
+        if (phoneItems != null && tachyonItems != null) {
+            for (DataItem phone : phoneItems) {
+                if (phone instanceof PhoneDataItem && ((PhoneDataItem) phone).getNumber() != null) {
+                    for (DataItem tachyonItem : tachyonItems) {
+                        if (((PhoneDataItem) phone).getNumber().equals(
+                                tachyonItem.getContentValues().getAsString(Data.DATA1))) {
+                            ((PhoneDataItem) phone).setTachyonReachable(true);
+                            ((PhoneDataItem) phone).setReachableDataItem(tachyonItem);
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
      * Class used to hold the About card and Contact cards' data model that gets generated
      * on a background thread. All data is from CP2.
      */
@@ -1574,6 +1603,7 @@
         Spannable smsContentDescription = null;
         Intent intent = null;
         boolean shouldApplyColor = true;
+        boolean shouldApplyThirdIconColor = true;
         Drawable alternateIcon = null;
         Intent alternateIntent = null;
         StringBuilder alternateContentDescription = new StringBuilder();
@@ -1746,6 +1776,9 @@
                 boolean isPresenceEnabled =
                         (videoCapability & CallUtil.VIDEO_CALLING_PRESENCE) != 0;
                 boolean isVideoEnabled = (videoCapability & CallUtil.VIDEO_CALLING_ENABLED) != 0;
+                // Check to ensure carrier presence indicates the number supports video calling.
+                int carrierPresence = dataItem.getCarrierPresence();
+                boolean isPresent = (carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) != 0;
 
                 if (CallUtil.isCallWithSubjectSupported(context)) {
                     thirdIcon = res.getDrawable(R.drawable.quantum_ic_perm_phone_msg_vd_theme_24);
@@ -1769,12 +1802,7 @@
                             phone.getFormattedPhoneNumber());
                     thirdExtras.putString(CallSubjectDialog.ARG_NUMBER_LABEL,
                             phoneLabel);
-                } else if (isVideoEnabled) {
-                    // Check to ensure carrier presence indicates the number supports video calling.
-                    int carrierPresence = dataItem.getCarrierPresence();
-                    boolean isPresent = (carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) != 0;
-
-                    if ((isPresenceEnabled && isPresent) || !isPresenceEnabled) {
+                } else if (isVideoEnabled && (!isPresenceEnabled || isPresent)) {
                         thirdIcon = res.getDrawable(R.drawable.quantum_ic_videocam_vd_theme_24);
                         thirdAction = Entry.ACTION_INTENT;
                         thirdIntent = CallUtil.getVideoCallIntent(phone.getNumber(),
@@ -1782,7 +1810,23 @@
                         thirdIntent.putExtra(EXTRA_ACTION_TYPE, ActionType.VIDEOCALL);
                         thirdContentDescription =
                                 res.getString(R.string.description_video_call);
-                    }
+                } else if (Flags.getInstance().getBoolean(Experiments.QUICK_CONTACT_VIDEO_CALL)
+                        && ((PhoneDataItem) dataItem).isTachyonReachable()) {
+                    final Intent tachyonIntent = new Intent(Intent.ACTION_VIEW);
+                    final Uri uri = ContentUris.withAppendedId(Data.CONTENT_URI,
+                            ((PhoneDataItem) dataItem).getReachableDataItem().getId());
+                    tachyonIntent.setDataAndType(uri, MIMETYPE_TACHYON);
+                    tachyonIntent.putExtra(EXTRA_ACTION_TYPE, ActionType.THIRD_PARTY);
+                    tachyonIntent.putExtra(EXTRA_THIRD_PARTY_ACTION, MIMETYPE_TACHYON);
+                    thirdIcon = ResolveCache.getInstance(context).getIcon(MIMETYPE_TACHYON,
+                            tachyonIntent);
+                    shouldApplyThirdIconColor = false;
+                    thirdAction = Entry.ACTION_INTENT;
+                    thirdIntent = new Intent("com.google.android.apps.tachyon.action.CALL");
+                    thirdIntent.setData(
+                            Uri.fromParts(PhoneAccount.SCHEME_TEL, phone.getNumber(), null));
+                    thirdContentDescription = ((PhoneDataItem) dataItem).getReachableDataItem()
+                            .getContentValues().getAsString(Data.DATA2);
                 }
             }
         } else if (dataItem instanceof EmailDataItem) {
@@ -1869,6 +1913,10 @@
                     aboutCardName.value = res.getString(R.string.about_card_title);
                 }
             }
+        } else if (Flags.getInstance().getBoolean(Experiments.QUICK_CONTACT_VIDEO_CALL)
+                && MIMETYPE_TACHYON.equals(dataItem.getMimeType())) {
+            // Skip these actions. They will be placed by the phone number.
+            return null;
         } else {
             // Custom DataItem
             header = dataItem.buildDataStringForDisplay(context, kind);
@@ -1881,7 +1929,6 @@
 
             if (intent != null) {
                 final String mimetype = intent.getType();
-
                 // Build advanced entry for known 3p types. Otherwise default to ResolveCache icon.
                 if (MIMETYPE_HANGOUTS.equals(mimetype)) {
                     // If a secondDataItem is available, use it to build an entry with
@@ -1963,7 +2010,7 @@
                         : smsContentDescription,
                 shouldApplyColor, isEditable,
                 entryContextMenuInfo, thirdIcon, thirdIntent, thirdContentDescription, thirdAction,
-                thirdExtras, iconResourceId);
+                thirdExtras, shouldApplyThirdIconColor, iconResourceId);
     }
 
     private List<Entry> dataItemsToEntries(List<DataItem> dataItems,
@@ -2264,6 +2311,7 @@
                     /* thirdContentDescription = */ null,
                     /* thirdAction = */ Entry.ACTION_NONE,
                     /* thirdActionExtras = */ null,
+                    /* shouldApplyThirdIconColor = */ true,
                     interaction.getIconResourceId()));
         }
         return entries;
@@ -2496,7 +2544,9 @@
                         /* isEditable = */ false, /* EntryContextMenuInfo = */ null,
                         /* thirdIcon = */ null, /* thirdIntent = */ null,
                         /* thirdContentDescription = */ null, /* thirdAction = */ Entry.ACTION_NONE,
-                        /* thirdExtras = */ null, R.drawable.quantum_ic_history_vd_theme_24);
+                        /* thirdExtras = */ null,
+                        /* shouldApplyThirdIconColor = */ true,
+                        R.drawable.quantum_ic_history_vd_theme_24);
 
                 final List<List<Entry>> permissionExplanationEntries = new ArrayList<>();
                 permissionExplanationEntries.add(new ArrayList<Entry>());