Merge "Make ContactPreferences use SharedPreferences instead of System settings (2/5)" into lmp-dev
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 30480c2..69d211e 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -20,6 +20,7 @@
 
     <original-package android:name="com.android.contacts" />
 
+    <uses-permission android:name="android.permission.CALL_PHONE" />
     <uses-permission android:name="android.permission.CALL_PRIVILEGED" />
     <uses-permission android:name="android.permission.READ_CONTACTS" />
     <uses-permission android:name="android.permission.WRITE_CONTACTS" />
diff --git a/res/drawable/action_bar_tab.xml b/res/drawable/action_bar_tab.xml
new file mode 100644
index 0000000..3982a3b
--- /dev/null
+++ b/res/drawable/action_bar_tab.xml
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<ripple xmlns:android="http://schemas.android.com/apk/res/android"
+    android:color="@color/tab_pressed_color">
+    <item>
+        <selector>
+            <item android:drawable="@drawable/tab_selected"
+                    android:state_focused="false"
+                    android:state_pressed="false"
+                    android:state_selected="true" />
+            <item android:drawable="@drawable/tab_selected_focused"
+                    android:state_focused="true"
+                    android:state_pressed="false"
+                    android:state_selected="true" />
+            <item android:drawable="@drawable/tab_unselected_focused"
+                    android:state_focused="true"
+                    android:state_pressed="false"
+                    android:state_selected="false" />
+            <item android:drawable="@drawable/tab_selected"
+                    android:state_selected="true" />
+            <item android:drawable="@color/tab_default_color" />
+        </selector>
+    </item>
+</ripple>
\ No newline at end of file
diff --git a/res/drawable/tab_selected.xml b/res/drawable/tab_selected.xml
new file mode 100644
index 0000000..9eec3c4
--- /dev/null
+++ b/res/drawable/tab_selected.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <!-- Tab selected underline -->
+    <item android:drawable="@color/tab_selected_color" />
+    <!-- Tab background -->
+    <item android:drawable="@color/tab_default_color"
+        android:bottom="@dimen/tab_selected_underline_height" />
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_selected_focused.xml b/res/drawable/tab_selected_focused.xml
new file mode 100644
index 0000000..ba5162a
--- /dev/null
+++ b/res/drawable/tab_selected_focused.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
+    <!-- Tab selected underline -->
+    <item android:drawable="@color/tab_selected_color" />
+    <!-- Tab background -->
+    <item android:drawable="@color/tab_default_color"
+        android:bottom="@dimen/tab_selected_underline_height" />
+    <!-- Focus rectangle -->
+    <item>
+        <shape android:shape="rectangle" >
+            <stroke
+                android:width="@dimen/tab_focused_stroke_width"
+                android:color="@color/focus_color" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/drawable/tab_unselected_focused.xml b/res/drawable/tab_unselected_focused.xml
new file mode 100644
index 0000000..1e4c6c3
--- /dev/null
+++ b/res/drawable/tab_unselected_focused.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+     Copyright (C) 2013 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+    <!-- Tab background -->
+    <item android:drawable="@color/tab_default_color" />
+    <!-- Focus rectangle -->
+    <item>
+        <shape android:shape="rectangle" >
+            <stroke
+                android:width="@dimen/tab_focused_stroke_width"
+                android:color="@color/focus_color" />
+        </shape>
+    </item>
+</layer-list>
\ No newline at end of file
diff --git a/res/layout/edit_date_picker.xml b/res/layout/edit_date_picker.xml
index b84a8d5..ce9a85f 100644
--- a/res/layout/edit_date_picker.xml
+++ b/res/layout/edit_date_picker.xml
@@ -20,7 +20,7 @@
 <Button
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:id="@+id/date_view"
-    style="?android:attr/spinnerStyle"
+    style="@style/SpinnerButtonStyle"
     android:layout_width="0dip"
     android:layout_height="@dimen/editor_min_line_item_height"
     android:layout_weight="1"
diff --git a/res/layout/expanding_entry_card_item.xml b/res/layout/expanding_entry_card_item.xml
index 99738a6..9195800 100644
--- a/res/layout/expanding_entry_card_item.xml
+++ b/res/layout/expanding_entry_card_item.xml
@@ -19,7 +19,7 @@
     style="@style/SelectableItem"
     android:layout_width="match_parent"
     android:layout_height="wrap_content"
-    android:paddingStart="@dimen/expanding_entry_card_item_padding_with_image_start"
+    android:paddingStart="@dimen/expanding_entry_card_item_padding_start"
     android:paddingEnd="@dimen/expanding_entry_card_item_padding_end"
     android:paddingTop="@dimen/expanding_entry_card_item_padding_top"
     android:paddingBottom="@dimen/expanding_entry_card_item_padding_bottom">
@@ -31,7 +31,8 @@
         android:layout_alignParentStart="true"
         android:layout_alignParentTop="true"
         android:layout_marginEnd="@dimen/expanding_entry_card_item_image_spacing"
-        android:scaleType="fitCenter" />
+        android:scaleType="fitCenter"
+        android:layout_marginTop="@dimen/expanding_entry_card_item_icon_margin_top" />
 
     <TextView
         android:id="@+id/header"
@@ -40,7 +41,7 @@
         android:layout_alignParentTop="true"
         android:layout_toEndOf="@+id/icon"
         android:singleLine="true"
-        android:textColor="@android:color/black" />
+        android:textColor="@color/quickcontact_entry_header_text_color" />
 
     <TextView
         android:id="@+id/sub_header"
@@ -48,7 +49,7 @@
         android:layout_height="wrap_content"
         android:layout_below="@+id/header"
         android:layout_toEndOf="@+id/icon_sub_header"
-        android:textColor="@android:color/black" />
+        android:textColor="@color/quickcontact_entry_sub_header_text_color" />
 
     <ImageView
         android:layout_width="wrap_content"
@@ -65,7 +66,7 @@
         android:id="@+id/text"
         android:layout_below="@+id/sub_header"
         android:layout_toEndOf="@+id/icon_text"
-        android:textColor="@android:color/darker_gray"/>
+        android:textColor="@color/quickcontact_entry_sub_header_text_color" />
 
     <ImageView
         android:layout_width="wrap_content"
@@ -76,4 +77,15 @@
         android:layout_marginTop="@dimen/expanding_entry_card_item_text_icon_margin_top"
         android:layout_marginEnd="@dimen/expanding_entry_card_item_text_icon_margin_right" />
 
-</RelativeLayout>
\ No newline at end of file
+    <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:layout_marginEnd="@dimen/expanding_entry_card_item_alternate_icon_margin_end"
+        android:layout_marginTop="@dimen/expanding_entry_card_item_icon_margin_top"
+        android:layout_marginBottom="@dimen/expanding_entry_card_item_alternate_icon_margin_bottom" />
+
+</RelativeLayout>
diff --git a/res/layout/expanding_entry_card_view.xml b/res/layout/expanding_entry_card_view.xml
index 8d8583d..b2ff58c 100644
--- a/res/layout/expanding_entry_card_view.xml
+++ b/res/layout/expanding_entry_card_view.xml
@@ -23,7 +23,8 @@
         android:lines="1"
         android:padding="@dimen/expanding_entry_card_title_padding"
         android:singleLine="true"
-        android:textSize="@dimen/expanding_entry_card_title_text_size" />
+        android:textSize="@dimen/expanding_entry_card_title_text_size"
+        android:visibility="gone" />
 
     <View
         android:layout_width="match_parent"
diff --git a/res/layout/item_group_membership.xml b/res/layout/item_group_membership.xml
index 84d1afe..7f58c2d 100644
--- a/res/layout/item_group_membership.xml
+++ b/res/layout/item_group_membership.xml
@@ -25,7 +25,7 @@
         layout="@layout/edit_kind_title" />
 
     <Button
-        style="?android:attr/spinnerStyle"
+        style="@style/SpinnerButtonStyle"
         android:id="@+id/group_list"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
diff --git a/res/layout/quickcontact_content.xml b/res/layout/quickcontact_content.xml
index 74d071c..e98e599 100644
--- a/res/layout/quickcontact_content.xml
+++ b/res/layout/quickcontact_content.xml
@@ -35,6 +35,17 @@
             android:background="@drawable/quickcontact_card_border">
             <com.android.contacts.quickcontact.ExpandingEntryCardView
                 style="@style/ExpandingEntryCardStyle"
+                android:id="@+id/no_contact_data_card"
+                android:layout_marginTop="@dimen/communication_card_marginTop"
+                android:visibility="gone" />
+        </FrameLayout>
+
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:background="@drawable/quickcontact_card_border">
+            <com.android.contacts.quickcontact.ExpandingEntryCardView
+                style="@style/ExpandingEntryCardStyle"
                 android:id="@+id/communication_card"
                 android:layout_marginTop="@dimen/communication_card_marginTop"
                 android:visibility="gone" />
diff --git a/res/layout/quickcontact_header.xml b/res/layout/quickcontact_header.xml
index b71bee0..1f73215 100644
--- a/res/layout/quickcontact_header.xml
+++ b/res/layout/quickcontact_header.xml
@@ -67,7 +67,7 @@
         android:textColor="@color/actionbar_text_color"
         android:maxLines="@integer/quickcontact_title_lines"
         android:ellipsize="end"
-        android:layout_gravity="bottom"
+        android:layout_gravity="bottom|start"
         android:textSize="@dimen/quickcontact_maximum_title_size"
         android:layout_marginStart="@dimen/quickcontact_title_initial_margin"
         android:layout_marginEnd="@dimen/quickcontact_title_initial_margin"
diff --git a/res/values/colors.xml b/res/values/colors.xml
index b44f34b..562241e 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -16,6 +16,8 @@
 <resources>
 
     <color name="quickcontact_name_detail_background">#66000000</color>
+    <color name="quickcontact_entry_sub_header_text_color">#737373</color>
+    <color name="quickcontact_entry_header_text_color">#202020</color>
 
     <!-- Color of the background of the contact detail and editor pages -->
     <color name="background_primary">#f5f5f5</color>
@@ -35,7 +37,6 @@
     <color name="action_bar_button_text_color">#FFFFFF</color>
 
     <!-- Color of the selected tab underline (overriding value in ContactsCommon) -->
-    <color name="tab_selected_color">#ffeeff41</color>
     <color name="contacts_accent_color">#00acc1</color>
 
     <!-- Color of the separator between entries in an ExpandingEntryCardView -->
@@ -61,7 +62,7 @@
     <!-- Background color of pinned header items. -->
     <color name="list_item_pinned_header_color">#f5f5f5</color>
 
-    <!-- The default color used for tinting photos when no color can be extracted via Palette,
-         this is Blue Grey 500 -->
-    <color name="quickcontact_default_photo_tint_color">#607D8B</color>
+    <color name="tab_default_color">@color/actionbar_background_color</color>
+    <color name="tab_pressed_color">@color/tab_selected_color</color>
+    <color name="tab_selected_color">#ffeeff41</color>
 </resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 0e296fe..877a68f 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -167,8 +167,7 @@
     <!-- Height of the separator between entries in an ExpandingEntryCardView -->
     <dimen name="expanding_entry_card_item_separator_height">1dp</dimen>
     <!-- Dimensions for an entry in ExpandingEntryCardView -->
-    <dimen name="expanding_entry_card_item_padding_start">67dp</dimen>
-    <dimen name="expanding_entry_card_item_padding_with_image_start">20dp</dimen>
+    <dimen name="expanding_entry_card_item_padding_start">20dp</dimen>
     <dimen name="expanding_entry_card_item_padding_end">20dp</dimen>
     <dimen name="expanding_entry_card_item_padding_top">16dp</dimen>
     <dimen name="expanding_entry_card_item_padding_bottom">16dp</dimen>
@@ -184,8 +183,15 @@
     <dimen name="expanding_entry_card_item_sub_header_icon_margin_right">4dp</dimen>
     <dimen name="expanding_entry_card_item_sub_header_icon_margin_bottom">14dp</dimen>
 
+    <dimen name="expanding_entry_card_item_icon_margin_top">8dp</dimen>
+    <dimen name="expanding_entry_card_item_alternate_icon_margin_end">0dp</dimen>
+    <dimen name="expanding_entry_card_item_alternate_icon_margin_bottom">10dp</dimen>
+
     <dimen name="people_activity_card_elevation">2dp</dimen>
 
     <dimen name="expanding_entry_card_item_icon_height">24dp</dimen>
     <dimen name="expanding_entry_card_item_icon_width">24dp</dimen>
+
+    <!-- Width of the box around a tab when the tab has focus -->
+    <dimen name="tab_focused_stroke_width">1dp</dimen>
 </resources>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0683190..494e115 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -651,9 +651,6 @@
     <!-- Button Label to see all on an ExpandingEntryCardView [CHAR LIMIT=40] -->
     <string name="expanding_entry_card_view_see_all">See all</string>
 
-    <!-- Title of communication card. [CHAR LIMIT=60] -->
-    <string name="communication_card_title">Contact</string>
-
     <!-- Title of recent card. [CHAR LIMIT=60] -->
     <string name="recent_card_title">Recent</string>
 
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 57c9358..a1a01d1 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -278,4 +278,10 @@
         <item name="android:layout_height">wrap_content</item>
     </style>
 
+    <style name="SpinnerButtonStyle" parent="@android:style/Widget.Material.Spinner">
+        <!-- When applying the spinner style to a Button we need to disable the shadow animation
+            on the button since the spinner background is transparent. Otherwise the spinner-button
+            will look ridiculous. -->
+        <item name="android:stateListAnimator">@null</item>
+    </style>
 </resources>
diff --git a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
index 2b2f032..633185b 100644
--- a/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
+++ b/src/com/android/contacts/quickcontact/ExpandingEntryCardView.java
@@ -24,11 +24,13 @@
 import android.content.Intent;
 import android.content.res.Resources;
 import android.graphics.ColorFilter;
+import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.text.TextUtils;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.LayoutInflater;
+import android.view.TouchDelegate;
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ViewTreeObserver;
@@ -60,16 +62,24 @@
         private final String mText;
         private final Drawable mTextIcon;
         private final Intent mIntent;
+        private final Drawable mAlternateIcon;
+        private final Intent mAlternateIntent;
+        private final String mAlternateContentDescription;
+        private final boolean mShouldApplyColor;
         private final boolean mIsEditable;
 
         public Entry(int viewId, Drawable icon, String header, String subHeader, String text,
-                Intent intent, boolean isEditable) {
-            this(viewId, icon, header, subHeader, null, text, null, intent, isEditable);
+                Intent intent, Drawable alternateIcon, Intent alternateIntent,
+                String alternateContentDescription, boolean shouldApplyColor,
+                boolean isEditable) {
+            this(viewId, icon, header, subHeader, null, text, null, intent, alternateIcon,
+                    alternateIntent, alternateContentDescription, shouldApplyColor, isEditable);
         }
 
         public Entry(int viewId, Drawable mainIcon, String header, String subHeader,
                 Drawable subHeaderIcon, String text, Drawable textIcon, Intent intent,
-                boolean isEditable) {
+                Drawable alternateIcon, Intent alternateIntent, String alternateContentDescription,
+                boolean shouldApplyColor, boolean isEditable) {
             mViewId = viewId;
             mIcon = mainIcon;
             mHeader = header;
@@ -78,6 +88,10 @@
             mText = text;
             mTextIcon = textIcon;
             mIntent = intent;
+            mAlternateIcon = alternateIcon;
+            mAlternateIntent = alternateIntent;
+            mAlternateContentDescription = alternateContentDescription;
+            mShouldApplyColor = shouldApplyColor;
             mIsEditable = isEditable;
         }
 
@@ -109,6 +123,22 @@
             return mIntent;
         }
 
+        Drawable getAlternateIcon() {
+            return mAlternateIcon;
+        }
+
+        Intent getAlternateIntent() {
+            return mAlternateIntent;
+        }
+
+        String getAlternateContentDescription() {
+            return mAlternateContentDescription;
+        }
+
+        boolean shouldApplyColor() {
+            return mShouldApplyColor;
+        }
+
         boolean isEditable() {
             return mIsEditable;
         }
@@ -187,6 +217,8 @@
         mIsExpanded = isExpanded;
         mEntryViews = new ArrayList<List<View>>(entries.size());
         mEntries = entries;
+        mNumEntries = 0;
+        mAllEntriesInflated = false;
         for (List<Entry> entryList : mEntries) {
             mNumEntries += entryList.size();
             mEntryViews.add(new ArrayList<View>());
@@ -270,16 +302,23 @@
             Resources resources = getResources();
             layoutParams.height = resources.getDimensionPixelSize(
                     R.dimen.expanding_entry_card_item_separator_height);
+            // The separator is aligned with the text in the entry. This is offset by a default
+            // margin. If there is an icon present, the icon's width and margin are added
+            int marginStart = resources.getDimensionPixelSize(
+                    R.dimen.expanding_entry_card_item_padding_start);
+            ImageView entryIcon = (ImageView) entry.findViewById(R.id.icon);
+            if (entryIcon.getDrawable() != null) {
+                int imageWidthAndMargin =
+                        resources.getDimensionPixelSize(
+                                R.dimen.expanding_entry_card_item_icon_width) +
+                        resources.getDimensionPixelSize(
+                                R.dimen.expanding_entry_card_item_image_spacing);
+                marginStart += imageWidthAndMargin;
+            }
             if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
-                layoutParams.rightMargin = resources.getDimensionPixelSize(
-                        R.dimen.expanding_entry_card_item_padding_start);
-                layoutParams.leftMargin = resources.getDimensionPixelSize(
-                        R.dimen.expanding_entry_card_item_padding_end);
+                layoutParams.rightMargin = marginStart;
             } else {
-                layoutParams.leftMargin = resources.getDimensionPixelSize(
-                        R.dimen.expanding_entry_card_item_padding_start);
-                layoutParams.rightMargin = resources.getDimensionPixelSize(
-                        R.dimen.expanding_entry_card_item_padding_end);
+                layoutParams.leftMargin = marginStart;
             }
             separator.setLayoutParams(layoutParams);
             mEntriesViewGroup.addView(separator);
@@ -343,6 +382,19 @@
         applyColor();
     }
 
+    public void setEntryHeaderColor(int color) {
+        if (mEntries != null) {
+            for (List<View> entryList : mEntryViews) {
+                for (View entryView : entryList) {
+                    TextView header = (TextView) entryView.findViewById(R.id.header);
+                    if (header != null) {
+                        header.setTextColor(color);
+                    }
+                }
+            }
+        }
+    }
+
     /**
      * The ColorFilter is passed in along with the color so that a new one only needs to be created
      * once for the entire activity.
@@ -362,9 +414,15 @@
             if (mEntries != null) {
                 for (List<Entry> entryList : mEntries) {
                     for (Entry entry : entryList) {
-                        Drawable icon = entry.getIcon();
-                        if (icon != null) {
-                            icon.setColorFilter(mThemeColorFilter);
+                        if (entry.shouldApplyColor()) {
+                            Drawable icon = entry.getIcon();
+                            if (icon != null) {
+                                icon.setColorFilter(mThemeColorFilter);
+                            }
+                        }
+                        Drawable alternateIcon = entry.getAlternateIcon();
+                        if (alternateIcon != null) {
+                            alternateIcon.setColorFilter(mThemeColorFilter);
                         }
                     }
                 }
@@ -379,47 +437,47 @@
 
     // TODO add accessibility content descriptions
     private View createEntryView(LayoutInflater layoutInflater, Entry entry) {
-        View view = layoutInflater.inflate(
+        final View view = layoutInflater.inflate(
                 R.layout.expanding_entry_card_item, this, false);
 
         view.setId(entry.getViewId());
 
-        ImageView icon = (ImageView) view.findViewById(R.id.icon);
+        final ImageView icon = (ImageView) view.findViewById(R.id.icon);
         if (entry.getIcon() != null) {
             icon.setImageDrawable(entry.getIcon());
         } else {
             icon.setVisibility(View.GONE);
         }
 
-        TextView header = (TextView) view.findViewById(R.id.header);
+        final TextView header = (TextView) view.findViewById(R.id.header);
         if (entry.getHeader() != null) {
             header.setText(entry.getHeader());
         } else {
             header.setVisibility(View.GONE);
         }
 
-        TextView subHeader = (TextView) view.findViewById(R.id.sub_header);
+        final TextView subHeader = (TextView) view.findViewById(R.id.sub_header);
         if (entry.getSubHeader() != null) {
             subHeader.setText(entry.getSubHeader());
         } else {
             subHeader.setVisibility(View.GONE);
         }
 
-        ImageView subHeaderIcon = (ImageView) view.findViewById(R.id.icon_sub_header);
+        final ImageView subHeaderIcon = (ImageView) view.findViewById(R.id.icon_sub_header);
         if (entry.getSubHeaderIcon() != null) {
             subHeaderIcon.setImageDrawable(entry.getSubHeaderIcon());
         } else {
             subHeaderIcon.setVisibility(View.GONE);
         }
 
-        TextView text = (TextView) view.findViewById(R.id.text);
+        final TextView text = (TextView) view.findViewById(R.id.text);
         if (entry.getText() != null) {
             text.setText(entry.getText());
         } else {
             text.setVisibility(View.GONE);
         }
 
-        ImageView textIcon = (ImageView) view.findViewById(R.id.icon_text);
+        final ImageView textIcon = (ImageView) view.findViewById(R.id.icon_text);
         if (entry.getTextIcon() != null) {
             textIcon.setImageDrawable(entry.getTextIcon());
         } else {
@@ -431,6 +489,39 @@
             view.setTag(entry.getIntent());
         }
 
+        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(entry.getAlternateIntent());
+            alternateIcon.setId(entry.getViewId());
+            alternateIcon.setVisibility(View.VISIBLE);
+            alternateIcon.setContentDescription(entry.getAlternateContentDescription());
+
+            // Expand the clickable area for alternate icon to be top to bottom and to right edge
+            // of the entry view
+            view.post(new Runnable() {
+                @Override
+                public void run() {
+                    final Rect entryRect = new Rect();
+                    view.getHitRect(entryRect);
+
+                    final Rect alternateIconRect = new Rect();
+                    alternateIcon.getHitRect(alternateIconRect);
+                    alternateIconRect.bottom = entryRect.bottom;
+                    alternateIconRect.top = entryRect.top;
+                    if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
+                        alternateIconRect.left = entryRect.left;
+                    } else {
+                        alternateIconRect.right = entryRect.right;
+                    }
+                    final TouchDelegate touchDelegate =
+                            new TouchDelegate(alternateIconRect, alternateIcon);
+                    view.setTouchDelegate(touchDelegate);
+                }
+            });
+        }
+
         return view;
     }
 
@@ -529,16 +620,17 @@
 
     /**
      * Sets the title text of this ExpandingEntryCardView.
-     * @param title The title to set. A null title will result in an empty string being set.
+     * @param title The title to set. A null title will result in the title being removed.
      */
     public void setTitle(String title) {
         if (mTitleTextView == null) {
             Log.e(TAG, "mTitleTextView is null");
         }
         if (title == null) {
-            mTitleTextView.setText("");
+            mTitleTextView.setVisibility(View.GONE);
         }
         mTitleTextView.setText(title);
+        mTitleTextView.setVisibility(View.VISIBLE);
     }
 
     public boolean shouldShow() {
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 0ef16d9..e453a4b 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -31,6 +31,8 @@
 import android.content.ContentValues;
 import android.content.Intent;
 import android.content.Loader;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
 import android.graphics.Bitmap;
 import android.graphics.Color;
 import android.graphics.PorterDuff;
@@ -127,7 +129,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;
 
@@ -162,7 +163,6 @@
     private static final int ANIMATION_STATUS_BAR_COLOR_CHANGE_DURATION = 150;
     private static final int REQUEST_CODE_CONTACT_EDITOR_ACTIVITY = 1;
     private static final int SCRIM_COLOR = Color.argb(0xB2, 0, 0, 0);
-    private static final String SCHEME_SMSTO = "smsto";
     private static final String MIMETYPE_SMS = "vnd.android-dir/mms-sms";
 
     /** This is the Intent action to install a shortcut in the launcher. */
@@ -188,6 +188,7 @@
     private ImageView mPhotoView;
     private View mTransparentView;
     private ExpandingEntryCardView mContactCard;
+    private ExpandingEntryCardView mNoContactDetailsCard;
     private ExpandingEntryCardView mRecentCard;
     private ExpandingEntryCardView mAboutCard;
     /**
@@ -206,13 +207,10 @@
     private AsyncTask<Void, Void, Pair<List<List<DataItem>>, Map<String, List<DataItem>>>>
             mEntriesAndActionsTask;
     private ColorDrawable mWindowScrim;
+    private MaterialColorMapUtils mMaterialColorMapUtils;
     private boolean mIsWaitingForOtherPieceOfExitAnimation;
     private boolean mIsExitAnimationInProgress;
     private boolean mHasComputedThemeColor;
-    private ComponentName mSmsComponent;
-
-    private static final int MIN_NUM_CONTACT_ENTRIES_SHOWN = 3;
-    private static final int MIN_NUM_COLLAPSED_RECENT_ENTRIES_SHOWN = 3;
 
     private Contact mContactData;
     private ContactLoader mContactLoader;
@@ -221,22 +219,14 @@
     private final ImageViewDrawableSetter mPhotoSetter = new ImageViewDrawableSetter();
 
     /**
-     * {@link #LEADING_MIMETYPES} and {@link #TRAILING_MIMETYPES} are used to sort MIME-types.
+     * {@link #LEADING_MIMETYPES} is used to sort MIME-types.
      *
      * <p>The MIME-types in {@link #LEADING_MIMETYPES} appear in the front of the dialog,
      * in the order specified here.</p>
-     *
-     * <p>The ones in {@link #TRAILING_MIMETYPES} appear in the end of the dialog, in the order
-     * specified here.</p>
-     *
-     * <p>The rest go between them, in the order in the array.</p>
      */
     private static final List<String> LEADING_MIMETYPES = Lists.newArrayList(
-            Phone.CONTENT_ITEM_TYPE, SipAddress.CONTENT_ITEM_TYPE, Email.CONTENT_ITEM_TYPE);
-
-    /** See {@link #LEADING_MIMETYPES}. */
-    private static final List<String> TRAILING_MIMETYPES = Lists.newArrayList(
-            StructuredPostal.CONTENT_ITEM_TYPE, Website.CONTENT_ITEM_TYPE);
+            Phone.CONTENT_ITEM_TYPE, SipAddress.CONTENT_ITEM_TYPE, Email.CONTENT_ITEM_TYPE,
+            StructuredPostal.CONTENT_ITEM_TYPE);
 
     private static final List<String> ABOUT_CARD_MIMETYPES = Lists.newArrayList(
             Event.CONTENT_ITEM_TYPE, GroupMembership.CONTENT_ITEM_TYPE, Identity.CONTENT_ITEM_TYPE,
@@ -267,13 +257,16 @@
     /** Id for the background Call Log Loader */
     private static final int LOADER_CALL_LOG_ID = 3;
     private static final int MAX_CALL_LOG_RETRIEVE = 3;
+    private static final int MIN_NUM_CONTACT_ENTRIES_SHOWN = 3;
+    private static final int MIN_NUM_COLLAPSED_RECENT_ENTRIES_SHOWN = 3;
+    private static final int CARD_ENTRY_ID_EDIT_CONTACT = -2;
 
 
     private static final int[] mRecentLoaderIds = new int[]{
         LOADER_SMS_ID,
         LOADER_CALENDAR_ID,
         LOADER_CALL_LOG_ID};
-    private Map<Integer, List<ContactInteraction>> mRecentLoaderResults;
+    private Map<Integer, List<ContactInteraction>> mRecentLoaderResults = new HashMap<>();
 
     private static final String FRAGMENT_TAG_SELECT_ACCOUNT = "select_account_fragment";
 
@@ -282,7 +275,11 @@
         public void onClick(View v) {
             // Data Id is stored as the entry view id
             final int dataId = v.getId();
-            Object intentObject = v.getTag();
+            if (dataId == CARD_ENTRY_ID_EDIT_CONTACT) {
+                editContact();
+                return;
+            }
+            final Object intentObject = v.getTag();
             if (intentObject == null || !(intentObject instanceof Intent)) {
                 Log.w(TAG, "Intent tag was not used correctly");
                 return;
@@ -294,7 +291,7 @@
             String usageType = DataUsageFeedback.USAGE_TYPE_CALL;
 
             final String scheme = intent.getData().getScheme();
-            if ((scheme != null && scheme.equals(SCHEME_SMSTO)) ||
+            if ((scheme != null && scheme.equals(CallUtil.SCHEME_SMSTO)) ||
                     (intent.getType() != null && intent.getType().equals(MIMETYPE_SMS))) {
                 usageType = DataUsageFeedback.USAGE_TYPE_SHORT_TEXT;
             }
@@ -436,6 +433,12 @@
         }
     };
 
+    /**
+     * Sorts among different mimetypes based off:
+     * 1. Times used
+     * 2. Last time used
+     * 3. Statically defined
+     */
     private final Comparator<List<DataItem>> mAmongstMimeTypeDataItemComparator =
             new Comparator<List<DataItem>> () {
         @Override
@@ -470,14 +473,6 @@
                     return 1;
                 }
             }
-            // Trailing types come last, so flip the returns
-            for (String mimeType : TRAILING_MIMETYPES) {
-                if (lhsMimeType.equals(mimeType)) {
-                    return 1;
-                } else if (rhsMimeType.equals(mimeType)) {
-                    return -1;
-                }
-            }
             return 0;
         }
     };
@@ -497,15 +492,16 @@
 
         setContentView(R.layout.quickcontact_activity);
 
-        mSmsComponent = PhoneCapabilityTester.getSmsComponent(this);
+        mMaterialColorMapUtils = new MaterialColorMapUtils(getResources());
 
         mContactCard = (ExpandingEntryCardView) findViewById(R.id.communication_card);
+        mNoContactDetailsCard = (ExpandingEntryCardView) findViewById(R.id.no_contact_data_card);
         mRecentCard = (ExpandingEntryCardView) findViewById(R.id.recent_card);
         mAboutCard = (ExpandingEntryCardView) findViewById(R.id.about_card);
         mScroller = (MultiShrinkScroller) findViewById(R.id.multiscroller);
 
+        mNoContactDetailsCard.setOnClickListener(mEntryClickHandler);
         mContactCard.setOnClickListener(mEntryClickHandler);
-        mContactCard.setTitle(getResources().getString(R.string.communication_card_title));
         mContactCard.setExpandButtonText(
         getResources().getString(R.string.expanding_entry_card_view_see_all));
 
@@ -574,7 +570,8 @@
                             // header tint before the MultiShrinkScroller has been measured will
                             // cause incorrect tinting calculations.
                             if (color != 0) {
-                                setThemeColor(MaterialColorMapUtils.calculateSecondaryColor(color));
+                                setThemeColor(mMaterialColorMapUtils
+                                        .calculatePrimaryAndSecondaryColor(color));
                             }
                         }
                     });
@@ -583,8 +580,8 @@
         Trace.endSection();
     }
 
-    protected void onActivityResult(int requestCode, int resultCode,
-            Intent data) {
+    @Override
+    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         if (requestCode == REQUEST_CODE_CONTACT_EDITOR_ACTIVITY &&
                 resultCode == ContactDeletionInteraction.RESULT_CODE_DELETED) {
             // The contact that we were showing has been deleted.
@@ -629,11 +626,11 @@
         } else if (oldLookupUri != mLookupUri) {
             // After copying a directory contact, the contact URI changes. Therefore,
             // we need to restart the loader and reload the new contact.
-            mContactLoader = (ContactLoader) getLoaderManager().restartLoader(
-                    LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
             for (int interactionLoaderId : mRecentLoaderIds) {
                 getLoaderManager().destroyLoader(interactionLoaderId);
             }
+            mContactLoader = (ContactLoader) getLoaderManager().restartLoader(
+                    LOADER_CONTACT_ID, null, mLoaderContactCallbacks);
         }
 
         NfcHandler.register(this, mLookupUri);
@@ -823,10 +820,60 @@
                 /* numInitialVisibleEntries = */ 1,
                 /* isExpanded = */ true,
                 mExpandingEntryCardViewListener);
+
+        if (contactCardEntries.size() == 0 && aboutCardEntries.size() == 0) {
+            initializeNoContactDetailCard();
+        } else {
+            mNoContactDetailsCard.setVisibility(View.GONE);
+        }
+
+        // If the Recent card is already initialized (all recent data is loaded), show the About
+        // card if it has entries. Otherwise About card visibility will be set in bindRecentData()
+        if (isAllRecentDataLoaded() && aboutCardEntries.size() > 0) {
+            mAboutCard.setVisibility(View.VISIBLE);
+        }
         Trace.endSection();
     }
 
     /**
+     * Create a card that shows "Add email" and "Add phone number" entries in grey.
+     */
+    private void initializeNoContactDetailCard() {
+        final Drawable phoneIcon = getResources().getDrawable(
+                R.drawable.ic_phone_24dp).mutate();
+        final Entry phonePromptEntry = new Entry(CARD_ENTRY_ID_EDIT_CONTACT,
+                phoneIcon, getString(R.string.quickcontact_add_phone_number),
+                /* subHeader = */ null, /* text = */ null, getEditContactIntent(),
+                /* alternateIcon = */ null, /* alternateIntent = */ null,
+                /* alternateContentDescription = */ null, /* shouldApplyColor = */ true,
+                /* isEditable = */ false);
+
+        final Drawable emailIcon = getResources().getDrawable(
+                R.drawable.ic_email_24dp).mutate();
+        final Entry emailPromptEntry = new Entry(CARD_ENTRY_ID_EDIT_CONTACT,
+                emailIcon, getString(R.string.quickcontact_add_email), /* subHeader = */ null,
+                /* text = */ null, getEditContactIntent(), /* alternateIcon = */ null,
+                /* alternateIntent = */ null, /* alternateContentDescription = */ null,
+                /* shouldApplyColor = */ true, /* isEditable = */ false);
+
+        final List<List<Entry>> promptEntries = new ArrayList<>();
+        promptEntries.add(new ArrayList<Entry>(1));
+        promptEntries.add(new ArrayList<Entry>(1));
+        promptEntries.get(0).add(phonePromptEntry);
+        promptEntries.get(1).add(emailPromptEntry);
+
+        final int subHeaderTextColor = getResources().getColor(
+                R.color.quickcontact_entry_sub_header_text_color);
+        final PorterDuffColorFilter greyColorFilter =
+                new PorterDuffColorFilter(subHeaderTextColor, PorterDuff.Mode.SRC_ATOP);
+        mNoContactDetailsCard.initialize(promptEntries, 2, /* isExpanded = */ false,
+                mExpandingEntryCardViewListener);
+        mNoContactDetailsCard.setVisibility(View.VISIBLE);
+        mNoContactDetailsCard.setEntryHeaderColor(subHeaderTextColor);
+        mNoContactDetailsCard.setColorAndFilter(subHeaderTextColor, greyColorFilter);
+    }
+
+    /**
      * Builds the {@link DataItem}s Map out of the Contact.
      * @param data The contact to build the data from.
      * @return A pair containing a list of data items sorted within mimetype and sorted
@@ -909,6 +956,10 @@
         String text = null;
         Drawable textIcon = null;
         Intent intent = null;
+        boolean shouldApplyColor = true;
+        Drawable alternateIcon = null;
+        Intent alternateIntent = null;
+        String alternateContentDescription = null;
         final boolean isEditable = false;
 
         DataKind kind = dataItem.getDataKind();
@@ -998,6 +1049,10 @@
                 if (PhoneCapabilityTester.isPhone(this)) {
                     intent = CallUtil.getCallIntent(phone.getNumber());
                 }
+                alternateIntent = new Intent(Intent.ACTION_SENDTO,
+                        Uri.fromParts(CallUtil.SCHEME_SMSTO, phone.getNumber(), null));
+                alternateIcon = getResources().getDrawable(R.drawable.ic_message_24dp);
+                alternateContentDescription = getResources().getString(R.string.sms_other);
             }
         } else if (dataItem instanceof EmailDataItem) {
             final EmailDataItem email = (EmailDataItem) dataItem;
@@ -1088,6 +1143,7 @@
                         if (icon != null) {
                             icon.mutate();
                         }
+                        shouldApplyColor = false;
                 }
             }
         }
@@ -1099,6 +1155,18 @@
             }
         }
 
+        if (alternateIntent != null) {
+            // Do not set the alternate intent is there are no resolves
+            if (!PhoneCapabilityTester.isIntentRegistered(this, alternateIntent)) {
+                alternateIntent = null;
+            }
+
+            // Attempt to use package manager to find a suitable content description if needed
+            if (TextUtils.isEmpty(alternateContentDescription)) {
+                alternateContentDescription = getIntentResolveLabel(alternateIntent);
+            }
+        }
+
         // If the Entry has no visual elements, return null
         if (icon == null && TextUtils.isEmpty(header) && TextUtils.isEmpty(subHeader) &&
                 subHeaderIcon == null && TextUtils.isEmpty(text) && textIcon == null) {
@@ -1108,8 +1176,9 @@
         final int dataId = dataItem.getId() > Integer.MAX_VALUE ?
                 -1 : (int) dataItem.getId();
 
-        return new Entry(dataId, icon, header, subHeader, subHeaderIcon, text, textIcon,
-                intent, isEditable);
+        return new Entry(dataId, icon, header, subHeader, subHeaderIcon, text, textIcon, intent,
+                alternateIcon, alternateIntent, alternateContentDescription, shouldApplyColor,
+                isEditable);
     }
 
     private List<Entry> dataItemsToEntries(List<DataItem> dataItems) {
@@ -1119,30 +1188,30 @@
             if (entry != null) {
                 entries.add(entry);
             }
-            // TODO merge secondary intents
-            if (dataItem instanceof PhoneDataItem) {
-                final PhoneDataItem phone = (PhoneDataItem) dataItem;
-                Intent smsIntent = null;
-                if (mSmsComponent != null) {
-                    smsIntent = new Intent(Intent.ACTION_SENDTO,
-                            Uri.fromParts(CallUtil.SCHEME_SMSTO, phone.getNumber(), null));
-                    smsIntent.setComponent(mSmsComponent);
-                }
-                final int dataId = dataItem.getId() > Integer.MAX_VALUE ?
-                        -1 : (int) dataItem.getId();
-                entries.add(new Entry(dataId,
-                        getResources().getDrawable(R.drawable.ic_message_24dp),
-                        getResources().getString(R.string.send_message),
-                        /* subHeader = */ null,
-                        /* text = */ phone.buildDataString(this,
-                                dataItem.getDataKind()),
-                        smsIntent,
-                        /* isEditable = */ false));
-            }
         }
         return entries;
     }
 
+    private String getIntentResolveLabel(Intent intent) {
+        final List<ResolveInfo> matches = getPackageManager().queryIntentActivities(intent,
+                PackageManager.MATCH_DEFAULT_ONLY);
+
+        // Pick first match, otherwise best found
+        ResolveInfo bestResolve = null;
+        final int size = matches.size();
+        if (size == 1) {
+            bestResolve = matches.get(0);
+        } else if (size > 1) {
+            bestResolve = ResolveCache.getInstance(this).getBestResolve(intent, matches);
+        }
+
+        if (bestResolve == null) {
+            return null;
+        }
+
+        return String.valueOf(bestResolve.loadLabel(getPackageManager()));
+    }
+
     /**
      * Asynchronously extract the most vibrant color from the PhotoView. Once extracted,
      * apply this tint to {@link MultiShrinkScroller}. This operation takes about 20-30ms
@@ -1161,16 +1230,15 @@
                     final Bitmap bitmap = ((BitmapDrawable) imageViewDrawable).getBitmap();
                     final int primaryColor = colorFromBitmap(bitmap);
                     if (primaryColor != 0) {
-                        return MaterialColorMapUtils.calculatePrimaryAndSecondaryColor(
+                        return mMaterialColorMapUtils.calculatePrimaryAndSecondaryColor(
                                 primaryColor);
                     }
                 }
                 if (imageViewDrawable instanceof LetterTileDrawable) {
                     final int primaryColor = ((LetterTileDrawable) imageViewDrawable).getColor();
-                    return MaterialColorMapUtils.calculateSecondaryColor(primaryColor);
+                    return mMaterialColorMapUtils.calculatePrimaryAndSecondaryColor(primaryColor);
                 }
-                return MaterialColorMapUtils.calculatePrimaryAndSecondaryColor(
-                        getResources().getColor(R.color.quickcontact_default_photo_tint_color));
+                return MaterialColorMapUtils.getDefaultPrimaryAndSecondaryColors(getResources());
             }
 
             @Override
@@ -1274,6 +1342,10 @@
                     interaction.getViewFooter(this),
                     interaction.getFooterIcon(this),
                     interaction.getIntent(),
+                    /* alternateIcon = */ null,
+                    /* alternateIntent = */ null,
+                    /* alternateContentDescription = */ null,
+                    /* shouldApplyColor = */ true,
                     /* isEditable = */ false));
         }
         return entries;
@@ -1283,6 +1355,7 @@
             new LoaderCallbacks<Contact>() {
         @Override
         public void onLoaderReset(Loader<Contact> loader) {
+            mContactData = null;
         }
 
         @Override
@@ -1388,11 +1461,6 @@
         @Override
         public void onLoadFinished(Loader<List<ContactInteraction>> loader,
                 List<ContactInteraction> data) {
-            if (mRecentLoaderResults == null) {
-                mRecentLoaderResults = new HashMap<Integer, List<ContactInteraction>>();
-            }
-            Log.v(TAG, "onLoadFinished ~ loader.getId() " + loader.getId() + " data.size() " +
-                    data.size());
             mRecentLoaderResults.put(loader.getId(), data);
 
             if (isAllRecentDataLoaded()) {
@@ -1404,7 +1472,6 @@
         public void onLoaderReset(Loader<List<ContactInteraction>> loader) {
             mRecentLoaderResults.remove(loader.getId());
         }
-
     };
 
     private boolean isAllRecentDataLoaded() {
@@ -1465,11 +1532,15 @@
         return mContactData != null && !mContactData.isDirectoryEntry();
     }
 
-    private void editContact() {
+    private Intent getEditContactIntent() {
         final Intent intent = new Intent(Intent.ACTION_EDIT, mLookupUri);
         mContactLoader.cacheResult();
         intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
-        startActivityForResult(intent, REQUEST_CODE_CONTACT_EDITOR_ACTIVITY);
+        return intent;
+    }
+
+    private void editContact() {
+        startActivityForResult(getEditContactIntent(), REQUEST_CODE_CONTACT_EDITOR_ACTIVITY);
     }
 
     private void toggleStar(MenuItem starredMenuItem) {
diff --git a/src/com/android/contacts/widget/MultiShrinkScroller.java b/src/com/android/contacts/widget/MultiShrinkScroller.java
index d9dac00..23481a7 100644
--- a/src/com/android/contacts/widget/MultiShrinkScroller.java
+++ b/src/com/android/contacts/widget/MultiShrinkScroller.java
@@ -872,8 +872,8 @@
         mToolbar.getBoundsOnScreen(largeTextViewRect);
         mInvisiblePlaceholderTextView.getBoundsOnScreen(invisiblePlaceholderTextViewRect);
         if (isLayoutRtl()) {
-            mCollapsedTitleStartMargin = invisiblePlaceholderTextViewRect.right
-                    - largeTextViewRect.right;
+            mCollapsedTitleStartMargin = largeTextViewRect.right
+                    - invisiblePlaceholderTextViewRect.right;
         } else {
             mCollapsedTitleStartMargin = invisiblePlaceholderTextViewRect.left
                     - largeTextViewRect.left;