Adds VOLTE icon and capabilities to QuickContact

bug: 16015752
Change-Id: I9970d5901f80fc56d7528d61da6b4e8b8c66e0c1
diff --git a/res/layout/expanding_entry_card_item.xml b/res/layout/expanding_entry_card_item.xml
index 4710df2..567c02c 100644
--- a/res/layout/expanding_entry_card_item.xml
+++ b/res/layout/expanding_entry_card_item.xml
@@ -82,16 +82,28 @@
         android:layout_marginTop="@dimen/expanding_entry_card_item_text_icon_margin_top"
         android:layout_marginEnd="@dimen/expanding_entry_card_item_text_icon_margin_right" />
 
-    <ImageView
-        android:id="@+id/icon_alternate"
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:layout_alignParentEnd="true"
-        android:layout_alignParentTop="true"
-        android:visibility="gone"
-        android:background="?android:attr/selectableItemBackgroundBorderless"
-        android:paddingTop="@dimen/expanding_entry_card_item_icon_margin_top"
-        android:paddingBottom="@dimen/expanding_entry_card_item_alternate_icon_margin_bottom"
-        android:layout_marginStart="@dimen/expanding_entry_card_item_alternate_icon_start_margin" />
+     <ImageView
+         android:id="@+id/icon_alternate"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentTop="true"
+         android:layout_toStartOf="@+id/third_icon"
+         android:layout_alignWithParentIfMissing="true"
+         android:visibility="gone"
+         android:background="?android:attr/selectableItemBackgroundBorderless"
+         android:paddingTop="@dimen/expanding_entry_card_item_icon_margin_top"
+         android:paddingBottom="@dimen/expanding_entry_card_item_alternate_icon_margin_bottom"
+         android:layout_marginStart="@dimen/expanding_entry_card_item_alternate_icon_start_margin" />
 
+     <ImageView
+         android:id="@+id/third_icon"
+         android:layout_width="wrap_content"
+         android:layout_height="wrap_content"
+         android:layout_alignParentEnd="true"
+         android:layout_alignParentTop="true"
+         android:visibility="gone"
+         android:background="?android:attr/selectableItemBackgroundBorderless"
+         android:paddingTop="@dimen/expanding_entry_card_item_icon_margin_top"
+         android:paddingBottom="@dimen/expanding_entry_card_item_alternate_icon_margin_bottom"
+         android:layout_marginStart="@dimen/expanding_entry_card_item_alternate_icon_start_margin" />
 </view>
diff --git a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
index 091971e..b219988 100644
--- a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
+++ b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
@@ -35,8 +35,9 @@
 import android.util.Log;
 import android.view.ContextMenu.ContextMenuInfo;
 import android.view.LayoutInflater;
-import android.view.TouchDelegate;
+import android.view.MotionEvent;
 import android.view.View;
+import android.view.ViewConfiguration;
 import android.view.View.OnCreateContextMenuListener;
 import android.view.ViewGroup;
 import android.widget.FrameLayout;
@@ -81,21 +82,26 @@
         private final boolean mShouldApplyColor;
         private final boolean mIsEditable;
         private final EntryContextMenuInfo mEntryContextMenuInfo;
+        private final Drawable mThirdIcon;
+        private final Intent mThirdIntent;
+        private final String mThirdContentDescription;
 
         public Entry(int id, Drawable icon, String header, String subHeader, String text,
                 Intent intent, Drawable alternateIcon, Intent alternateIntent,
                 String alternateContentDescription, boolean shouldApplyColor,
-                boolean isEditable, EntryContextMenuInfo entryContextMenuInfo) {
+                boolean isEditable, EntryContextMenuInfo entryContextMenuInfo,
+                Drawable thirdIcon, Intent thirdIntent, String thirdContentDescription) {
             this(id, icon, header, subHeader, null, text, null, intent, alternateIcon,
                     alternateIntent, alternateContentDescription, shouldApplyColor, isEditable,
-                    entryContextMenuInfo);
+                    entryContextMenuInfo, thirdIcon, thirdIntent, thirdContentDescription);
         }
 
         public Entry(int id, Drawable mainIcon, String header, String subHeader,
                 Drawable subHeaderIcon, String text, Drawable textIcon, Intent intent,
                 Drawable alternateIcon, Intent alternateIntent, String alternateContentDescription,
                 boolean shouldApplyColor, boolean isEditable,
-                EntryContextMenuInfo entryContextMenuInfo) {
+                EntryContextMenuInfo entryContextMenuInfo, Drawable thirdIcon, Intent thirdIntent,
+                String thirdContentDescription) {
             mId = id;
             mIcon = mainIcon;
             mHeader = header;
@@ -110,6 +116,9 @@
             mShouldApplyColor = shouldApplyColor;
             mIsEditable = isEditable;
             mEntryContextMenuInfo = entryContextMenuInfo;
+            mThirdIcon = thirdIcon;
+            mThirdIntent = thirdIntent;
+            mThirdContentDescription = thirdContentDescription;
         }
 
         Drawable getIcon() {
@@ -167,6 +176,18 @@
         EntryContextMenuInfo getEntryContextMenuInfo() {
             return mEntryContextMenuInfo;
         }
+
+        Drawable getThirdIcon() {
+            return mThirdIcon;
+        }
+
+        Intent getThirdIntent() {
+            return mThirdIntent;
+        }
+
+        String getThirdContentDescription() {
+            return mThirdContentDescription;
+        }
     }
 
     public interface ExpandingEntryCardViewListener {
@@ -541,6 +562,10 @@
                         if (alternateIcon != null) {
                             alternateIcon.setColorFilter(mThemeColorFilter);
                         }
+                        Drawable thirdIcon = entry.getThirdIcon();
+                        if (thirdIcon != null) {
+                            thirdIcon.setColorFilter(mThemeColorFilter);
+                        }
                     }
                 }
             }
@@ -621,36 +646,6 @@
             header.setLayoutParams(headerLayoutParams);
         }
 
-        final ImageView alternateIcon = (ImageView) view.findViewById(R.id.icon_alternate);
-        if (entry.getAlternateIcon() != null && entry.getAlternateIntent() != null) {
-            alternateIcon.setImageDrawable(entry.getAlternateIcon());
-            alternateIcon.setOnClickListener(mOnClickListener);
-            alternateIcon.setTag(new EntryTag(entry.getId(), entry.getAlternateIntent()));
-            alternateIcon.setVisibility(View.VISIBLE);
-            alternateIcon.setContentDescription(entry.getAlternateContentDescription());
-
-            // Expand the clickable area for alternate icon to be top to bottom and to end edge
-            // of the entry view
-            view.post(new Runnable() {
-                @Override
-                public void run() {
-                    final Rect alternateIconRect = new Rect();
-                    alternateIcon.getHitRect(alternateIconRect);
-
-                    alternateIconRect.bottom = view.getHeight();
-                    alternateIconRect.top = 0;
-                    if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-                        alternateIconRect.left = 0;
-                    } else {
-                        alternateIconRect.right = view.getWidth();
-                    }
-                    final TouchDelegate touchDelegate =
-                            new TouchDelegate(alternateIconRect, alternateIcon);
-                    view.setTouchDelegate(touchDelegate);
-                }
-            });
-        }
-
         // Adjust the top padding size for entries with an invisible icon. The padding depends on
         // if there is a sub header or text section
         if (iconVisibility == View.INVISIBLE &&
@@ -666,6 +661,27 @@
                     view.getPaddingBottom());
         }
 
+        final ImageView alternateIcon = (ImageView) view.findViewById(R.id.icon_alternate);
+        final ImageView thirdIcon = (ImageView) view.findViewById(R.id.third_icon);
+
+        if (entry.getAlternateIcon() != null && entry.getAlternateIntent() != null) {
+            alternateIcon.setImageDrawable(entry.getAlternateIcon());
+            alternateIcon.setOnClickListener(mOnClickListener);
+            alternateIcon.setTag(new EntryTag(entry.getId(), entry.getAlternateIntent()));
+            alternateIcon.setVisibility(View.VISIBLE);
+            alternateIcon.setContentDescription(entry.getAlternateContentDescription());
+        }
+
+        if (entry.getThirdIcon() != null && entry.getThirdIntent() != null) {
+            thirdIcon.setImageDrawable(entry.getThirdIcon());
+            thirdIcon.setOnClickListener(mOnClickListener);
+            thirdIcon.setTag(new EntryTag(entry.getId(), entry.getThirdIntent()));
+            thirdIcon.setVisibility(View.VISIBLE);
+            thirdIcon.setContentDescription(entry.getThirdContentDescription());
+        }
+
+        // Set a custom touch listener for expanding the extra icon touch areas
+        view.setOnTouchListener(new EntryTouchListener(view, alternateIcon, thirdIcon));
         view.setOnCreateContextMenuListener(mOnCreateContextMenuListener);
 
         return view;
@@ -920,4 +936,103 @@
             return mIntent;
         }
     }
+
+    /**
+     * This custom touch listener increases the touch area for the second and third icons, if
+     * they are present. This is necessary to maintain other properties on an entry view, like
+     * using a top padding on entry. Based off of {@link android.view.TouchDelegate}
+     */
+    private static final class EntryTouchListener implements View.OnTouchListener {
+        private final View mEntry;
+        private final ImageView mAlternateIcon;
+        private final ImageView mThirdIcon;
+        /** mTouchedView locks in a view on touch down */
+        private View mTouchedView;
+        /** mSlop adds some space to account for touches that are just outside the hit area */
+        private int mSlop;
+
+        public EntryTouchListener(View entry, ImageView alternateIcon, ImageView thirdIcon) {
+            mEntry = entry;
+            mAlternateIcon = alternateIcon;
+            mThirdIcon = thirdIcon;
+            mSlop = ViewConfiguration.get(entry.getContext()).getScaledTouchSlop();
+        }
+
+        @Override
+        public boolean onTouch(View v, MotionEvent event) {
+            View touchedView = mTouchedView;
+            boolean sendToTouched = false;
+            boolean hit = true;
+            boolean handled = false;
+
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_DOWN:
+                    if (hitThirdIcon(event)) {
+                        mTouchedView = mThirdIcon;
+                        sendToTouched = true;
+                    } else if (hitAlternateIcon(event)) {
+                        mTouchedView = mAlternateIcon;
+                        sendToTouched = true;
+                    } else {
+                        mTouchedView = mEntry;
+                        sendToTouched = false;
+                    }
+                    touchedView = mTouchedView;
+                    break;
+                case MotionEvent.ACTION_UP:
+                case MotionEvent.ACTION_MOVE:
+                    sendToTouched = mTouchedView != null && mTouchedView != mEntry;
+                    if (sendToTouched) {
+                        final Rect slopBounds = new Rect();
+                        touchedView.getHitRect(slopBounds);
+                        slopBounds.inset(-mSlop, -mSlop);
+                        if (!slopBounds.contains((int) event.getX(), (int) event.getY())) {
+                            hit = false;
+                        }
+                    }
+                    break;
+                case MotionEvent.ACTION_CANCEL:
+                    sendToTouched = mTouchedView != null && mTouchedView != mEntry;
+                    mTouchedView = null;
+                    break;
+            }
+            if (sendToTouched) {
+                if (hit) {
+                    event.setLocation(touchedView.getWidth() / 2, touchedView.getHeight() / 2);
+                } else {
+                    // Offset event coordinates to be outside the target view (in case it does
+                    // something like tracking pressed state)
+                    event.setLocation(-(mSlop * 2), -(mSlop * 2));
+                }
+                handled = touchedView.dispatchTouchEvent(event);
+            }
+            return handled;
+        }
+
+        private boolean hitThirdIcon(MotionEvent event) {
+            if (mEntry.isLayoutRtl()) {
+                return mThirdIcon.getVisibility() == View.VISIBLE &&
+                        event.getX() < mThirdIcon.getRight();
+            } else {
+                return mThirdIcon.getVisibility() == View.VISIBLE &&
+                        event.getX() > mThirdIcon.getLeft();
+            }
+        }
+
+        /**
+         * Should be used after checking if third icon was hit
+         */
+        private boolean hitAlternateIcon(MotionEvent event) {
+            // LayoutParams used to add the start margin to the touch area
+            final RelativeLayout.LayoutParams alternateIconParams =
+                    (RelativeLayout.LayoutParams) mAlternateIcon.getLayoutParams();
+            if (mEntry.isLayoutRtl()) {
+                return mAlternateIcon.getVisibility() == View.VISIBLE &&
+                        event.getX() < mAlternateIcon.getRight() + alternateIconParams.rightMargin;
+            } else {
+                return mAlternateIcon.getVisibility() == View.VISIBLE &&
+                        event.getX() > mAlternateIcon.getLeft() - alternateIconParams.leftMargin;
+            }
+        }
+    }
 }
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 50e4ff7..5964aeb 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -135,7 +135,6 @@
 import com.android.contacts.util.StructuredPostalUtils;
 import com.android.contacts.widget.MultiShrinkScroller;
 import com.android.contacts.widget.MultiShrinkScroller.MultiShrinkScrollerListener;
-
 import com.google.common.base.Preconditions;
 import com.google.common.collect.Lists;
 
@@ -185,6 +184,8 @@
     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 CALL_ORIGIN_QUICK_CONTACTS_ACTIVITY =
+            "com.android.contacts.quickcontact.QuickContactActivity";
 
     /**
      * The URI used to load the the Contact. Once the contact is loaded, use Contact#getLookupUri()
@@ -937,7 +938,10 @@
                     /* shouldApplyColor = */ false,
                     /* isEditable = */ false,
                     /* EntryContextMenuInfo = */ new EntryContextMenuInfo(phoneticName,
-                            getResources().getString(R.string.name_phonetic)));
+                            getResources().getString(R.string.name_phonetic)),
+                    /* thirdIcon = */ null,
+                    /* thirdIntent = */ null,
+                    /* thirdContentDescription = */ null);
             List<Entry> phoneticList = new ArrayList<>();
             phoneticList.add(phoneticEntry);
             // Phonetic name comes after nickname. Check to see if the first entry type is nickname
@@ -987,7 +991,9 @@
                 /* subHeader = */ null, /* text = */ null, getEditContactIntent(),
                 /* alternateIcon = */ null, /* alternateIntent = */ null,
                 /* alternateContentDescription = */ null, /* shouldApplyColor = */ true,
-                /* isEditable = */ false, /* EntryContextMenuInfo = */ null);
+                /* isEditable = */ false, /* EntryContextMenuInfo = */ null,
+                /* thirdIcon = */ null, /* thirdIntent = */ null,
+                /* thirdContentDescription = */ null);
 
         final Drawable emailIcon = getResources().getDrawable(
                 R.drawable.ic_email_24dp).mutate();
@@ -996,7 +1002,8 @@
                 /* text = */ null, getEditContactIntent(), /* alternateIcon = */ null,
                 /* alternateIntent = */ null, /* alternateContentDescription = */ null,
                 /* shouldApplyColor = */ true, /* isEditable = */ false,
-                /* EntryContextMenuInfo = */ null);
+                /* EntryContextMenuInfo = */ null, /* thirdIcon = */ null,
+                /* thirdIntent = */ null, /* thirdContentDescription = */ null);
 
         final List<List<Entry>> promptEntries = new ArrayList<>();
         promptEntries.add(new ArrayList<Entry>(1));
@@ -1157,6 +1164,9 @@
         String alternateContentDescription = null;
         final boolean isEditable = false;
         EntryContextMenuInfo entryContextMenuInfo = null;
+        Drawable thirdIcon = null;
+        Intent thirdIntent = null;
+        String thirdContentDescription = null;
 
         context = context.getApplicationContext();
         DataKind kind = dataItem.getDataKind();
@@ -1273,8 +1283,18 @@
                 }
                 alternateIntent = new Intent(Intent.ACTION_SENDTO,
                         Uri.fromParts(CallUtil.SCHEME_SMSTO, phone.getNumber(), null));
+
                 alternateIcon = context.getResources().getDrawable(R.drawable.ic_message_24dp);
                 alternateContentDescription = context.getResources().getString(R.string.sms_other);
+
+                // Add video call button if supported
+                if (CallUtil.isVideoEnabled()) {
+                    thirdIcon = context.getResources().getDrawable(R.drawable.ic_videocam);
+                    thirdIntent = CallUtil.getVideoCallIntent(phone.getNumber(),
+                            CALL_ORIGIN_QUICK_CONTACTS_ACTIVITY);
+                    thirdContentDescription =
+                            context.getResources().getString(R.string.description_video_call);
+                }
             }
         } else if (dataItem instanceof EmailDataItem) {
             final EmailDataItem email = (EmailDataItem) dataItem;
@@ -1406,7 +1426,7 @@
 
         return new Entry(dataId, icon, header, subHeader, subHeaderIcon, text, textIcon, intent,
                 alternateIcon, alternateIntent, alternateContentDescription, shouldApplyColor,
-                isEditable, entryContextMenuInfo);
+                isEditable, entryContextMenuInfo, thirdIcon, thirdIntent, thirdContentDescription);
     }
 
     private List<Entry> dataItemsToEntries(List<DataItem> dataItems,
@@ -1574,7 +1594,10 @@
                     /* alternateContentDescription = */ null,
                     /* shouldApplyColor = */ true,
                     /* isEditable = */ false,
-                    /* EntryContextMenuInfo = */ null));
+                    /* EntryContextMenuInfo = */ null,
+                    /* thirdIcon = */ null,
+                    /* thirdIntent = */ null,
+                    /* thirdContentDescription = */ null));
         }
         return entries;
     }