Merge "Add "SCHEME_VOICEMAIL" as a constant in CallUtil." into lmp-dev
diff --git a/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java b/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java
index 8aed989..ab2d395 100644
--- a/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java
+++ b/TestCommon/src/com/android/contacts/common/test/mocks/MockAccountTypeManager.java
@@ -19,6 +19,7 @@
 import com.android.contacts.common.model.account.AccountType;
 import com.android.contacts.common.model.account.AccountTypeWithDataSet;
 import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.model.account.BaseAccountType;
 import com.google.common.base.Objects;
 import com.google.common.collect.Lists;
 import com.google.common.collect.Maps;
@@ -42,13 +43,21 @@
 
     @Override
     public AccountType getAccountType(AccountTypeWithDataSet accountTypeWithDataSet) {
+        // Add fallback accountType to mimic the behavior of AccountTypeManagerImpl
+        AccountType mFallbackAccountType = new BaseAccountType() {
+            @Override
+            public boolean areContactsWritable() {
+                return false;
+            }
+        };
+        mFallbackAccountType.accountType = "fallback";
         for (AccountType type : mTypes) {
             if (Objects.equal(accountTypeWithDataSet.accountType, type.accountType)
                     && Objects.equal(accountTypeWithDataSet.dataSet, type.dataSet)) {
                 return type;
             }
         }
-        return null;
+        return mFallbackAccountType;
     }
 
     @Override
diff --git a/src/com/android/contacts/common/ContactsUtils.java b/src/com/android/contacts/common/ContactsUtils.java
index 7fa1d1c..4c31367 100644
--- a/src/com/android/contacts/common/ContactsUtils.java
+++ b/src/com/android/contacts/common/ContactsUtils.java
@@ -174,12 +174,12 @@
         final boolean isEmail = im.isCreatedFromEmail();
 
         if (!isEmail && !im.isProtocolValid()) {
-            return null;
+            return new Pair<>(null, null);
         }
 
         final String data = im.getData();
         if (TextUtils.isEmpty(data)) {
-            return null;
+            return new Pair<>(null, null);
         }
 
         final int protocol = isEmail ? Im.PROTOCOL_GOOGLE_TALK : im.getProtocol();
diff --git a/src/com/android/contacts/common/list/PinnedHeaderListView.java b/src/com/android/contacts/common/list/PinnedHeaderListView.java
index 9f064cd..cc31cff 100644
--- a/src/com/android/contacts/common/list/PinnedHeaderListView.java
+++ b/src/com/android/contacts/common/list/PinnedHeaderListView.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.graphics.Canvas;
-import android.graphics.Rect;
 import android.graphics.RectF;
 import android.util.AttributeSet;
 import android.view.MotionEvent;
@@ -513,15 +512,16 @@
         if (hasVisibleHeaders) {
             canvas.restore();
 
-            // If the first item is visible, if it has a positive top use that for the first
-            // header's y value. This way, the header inherits any padding applied to the list view.
+            // If the first item is visible and if it has a positive top that is greater than the
+            // first header's assigned y-value, use that for the first header's y value. This way,
+            // the header inherits any padding applied to the list view.
             if (mSize > 0 && getFirstVisiblePosition() == 0) {
                 View firstChild = getChildAt(0);
                 PinnedHeader firstHeader = mHeaders[0];
 
                 if (firstHeader != null) {
                     int firstHeaderTop = firstChild != null ? firstChild.getTop() : 0;
-                    firstHeader.y = firstHeaderTop;
+                    firstHeader.y = Math.max(firstHeader.y, firstHeaderTop);
                 }
             }
 
diff --git a/src/com/android/contacts/common/list/ShortcutIntentBuilder.java b/src/com/android/contacts/common/list/ShortcutIntentBuilder.java
index 1831d5a..b1a3061 100644
--- a/src/com/android/contacts/common/list/ShortcutIntentBuilder.java
+++ b/src/com/android/contacts/common/list/ShortcutIntentBuilder.java
@@ -90,8 +90,7 @@
     private final Context mContext;
     private int mIconSize;
     private final int mIconDensity;
-    private final int mBorderWidth;
-    private final int mBorderColor;
+    private final int mOverlayTextBackgroundColor;
     private final Resources mResources;
 
     /**
@@ -130,9 +129,7 @@
             mIconSize = am.getLauncherLargeIconSize();
         }
         mIconDensity = am.getLauncherLargeIconDensity();
-        mBorderWidth = mResources.getDimensionPixelOffset(
-                R.dimen.shortcut_icon_border_width);
-        mBorderColor = mResources.getColor(R.color.shortcut_overlay_text_background);
+        mOverlayTextBackgroundColor = mResources.getColor(R.color.shortcut_overlay_text_background);
     }
 
     public void createContactShortcutIntent(Uri contactUri) {
@@ -329,17 +326,6 @@
         mListener.onShortcutIntentCreated(uri, intent);
     }
 
-    private void drawBorder(Canvas canvas, Rect dst) {
-        // Darken the border
-        final Paint workPaint = new Paint();
-        workPaint.setColor(mBorderColor);
-        workPaint.setStyle(Paint.Style.STROKE);
-        // The stroke is drawn centered on the rect bounds, and since half will be drawn outside the
-        // bounds, we need to double the width for it to appear as intended.
-        workPaint.setStrokeWidth(mBorderWidth * 2);
-        canvas.drawRect(dst, workPaint);
-    }
-
     private Bitmap generateQuickContactIcon(Drawable photo) {
 
         // Setup the drawing classes
@@ -377,8 +363,7 @@
         Bitmap phoneIcon = ((BitmapDrawable) r.getDrawableForDensity(actionResId, mIconDensity))
                 .getBitmap();
 
-        // Setup the drawing classes
-        Bitmap icon = Bitmap.createBitmap(mIconSize, mIconSize, Bitmap.Config.ARGB_8888);
+        Bitmap icon = generateQuickContactIcon(photo);
         Canvas canvas = new Canvas(icon);
 
         // Copy in the photo
@@ -387,11 +372,6 @@
         photoPaint.setFilterBitmap(true);
         Rect dst = new Rect(0, 0, mIconSize, mIconSize);
 
-        photo.setBounds(dst);
-        photo.draw(canvas);
-
-        drawBorder(canvas, dst);
-
         // Create an overlay for the phone number type
         CharSequence overlay = Phone.getTypeLabel(r, phoneType, phoneLabel);
 
@@ -405,18 +385,15 @@
 
             // First fill in a darker background around the text to be drawn
             final Paint workPaint = new Paint();
-            workPaint.setColor(mBorderColor);
+            workPaint.setColor(mOverlayTextBackgroundColor);
             workPaint.setStyle(Paint.Style.FILL);
             final int textPadding = r
                     .getDimensionPixelOffset(R.dimen.shortcut_overlay_text_background_padding);
             final int textBandHeight = (fmi.descent - fmi.ascent) + textPadding * 2;
-            dst.set(0 + mBorderWidth, mIconSize - textBandHeight, mIconSize - mBorderWidth,
-                    mIconSize - mBorderWidth);
+            dst.set(0, mIconSize - textBandHeight, mIconSize, mIconSize);
             canvas.drawRect(dst, workPaint);
 
-            final float sidePadding = mBorderWidth;
-            overlay = TextUtils.ellipsize(overlay, textPaint, mIconSize - 2 * sidePadding,
-                    TruncateAt.END);
+            overlay = TextUtils.ellipsize(overlay, textPaint, mIconSize, TruncateAt.END);
             final float textWidth = textPaint.measureText(overlay, 0, overlay.length());
             canvas.drawText(overlay, 0, overlay.length(), (mIconSize - textWidth) / 2, mIconSize
                     - fmi.descent - textPadding, textPaint);
@@ -427,7 +404,6 @@
         int iconWidth = icon.getWidth();
         dst.set(iconWidth - ((int) (20 * density)), -1,
                 iconWidth, ((int) (19 * density)));
-        dst.offset(-mBorderWidth, mBorderWidth);
         canvas.drawBitmap(phoneIcon, src, dst, photoPaint);
 
         canvas.setBitmap(null);
diff --git a/src/com/android/contacts/common/model/ContactLoader.java b/src/com/android/contacts/common/model/ContactLoader.java
index 5f9c164..eba825b 100644
--- a/src/com/android/contacts/common/model/ContactLoader.java
+++ b/src/com/android/contacts/common/model/ContactLoader.java
@@ -356,7 +356,7 @@
         final long directoryId =
                 Long.valueOf(uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY));
 
-        final String displayName = json.getString(Contacts.DISPLAY_NAME);
+        final String displayName = json.optString(Contacts.DISPLAY_NAME);
         final String altDisplayName = json.optString(
                 Contacts.DISPLAY_NAME_ALTERNATIVE, displayName);
         final int displayNameSource = json.getInt(Contacts.DISPLAY_NAME_SOURCE);
diff --git a/src/com/android/contacts/common/widget/FloatingActionButtonController.java b/src/com/android/contacts/common/widget/FloatingActionButtonController.java
index 773e024..679eb7b 100644
--- a/src/com/android/contacts/common/widget/FloatingActionButtonController.java
+++ b/src/com/android/contacts/common/widget/FloatingActionButtonController.java
@@ -34,8 +34,9 @@
     public static final int ALIGN_QUARTER_END = 1;
     public static final int ALIGN_END = 2;
 
-    private static final int FAB_FADE_DURATION = 266;
-    private static final int FAB_FADE_IN_DELAY = 100;
+    private static final int FAB_SCALE_IN_DURATION = 266;
+    private static final int FAB_SCALE_IN_FADE_IN_DELAY = 100;
+    private static final int FAB_ICON_FADE_OUT_DURATION = 66;
 
     private final int mAnimationDuration;
     private final int mFloatingActionButtonWidth;
@@ -138,10 +139,13 @@
     /**
      * Scales the floating action button from no height and width to its actual dimensions. This is
      * an animation for showing the floating action button.
+     * @param delayMs The delay for the effect, in milliseconds.
      */
-    public void scaleIn() {
-        AnimUtils.scaleIn(mFloatingActionButtonContainer, mAnimationDuration);
-        AnimUtils.fadeIn(mFloatingActionButton, FAB_FADE_DURATION, FAB_FADE_IN_DELAY, null);
+    public void scaleIn(int delayMs) {
+        setVisible(true);
+        AnimUtils.scaleIn(mFloatingActionButtonContainer, FAB_SCALE_IN_DURATION, delayMs);
+        AnimUtils.fadeIn(mFloatingActionButton, FAB_SCALE_IN_DURATION,
+                delayMs + FAB_SCALE_IN_FADE_IN_DELAY, null);
     }
 
     /**
@@ -150,6 +154,9 @@
      */
     public void scaleOut() {
         AnimUtils.scaleOut(mFloatingActionButtonContainer, mAnimationDuration);
+        // Fade out the icon faster than the scale out animation, so that the icon scaling is less
+        // obvious. We don't want it to scale, but the resizing the container is not as performant.
+        AnimUtils.fadeOut(mFloatingActionButton, FAB_ICON_FADE_OUT_DURATION, null);
     }
 
     /**
@@ -182,17 +189,6 @@
         return result;
     }
 
-    /**
-     * Manually translates the FAB without animation.
-     *
-     * @param translationX The x distance to translate.
-     * @param translationY The y distance to translate.
-     */
-    public void manuallyTranslate(int translationX, int translationY) {
-        mFloatingActionButtonContainer.setTranslationX(translationX);
-        mFloatingActionButtonContainer.setTranslationY(translationY);
-    }
-
     private boolean isLayoutRtl() {
         return mFloatingActionButtonContainer.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
     }
diff --git a/tests/src/com/android/contacts/common/model/ContactLoaderTest.java b/tests/src/com/android/contacts/common/model/ContactLoaderTest.java
index a16cda9..1c65d7b 100644
--- a/tests/src/com/android/contacts/common/model/ContactLoaderTest.java
+++ b/tests/src/com/android/contacts/common/model/ContactLoaderTest.java
@@ -18,6 +18,7 @@
 
 import android.content.ContentUris;
 import android.net.Uri;
+import android.provider.ContactsContract;
 import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
 import android.provider.ContactsContract.CommonDataKinds.StructuredName;
 import android.provider.ContactsContract.Contacts;
@@ -36,12 +37,21 @@
 import com.android.contacts.common.model.account.BaseAccountType;
 import com.android.contacts.common.testing.InjectedServices;
 import com.android.contacts.common.test.mocks.MockAccountTypeManager;
+import com.android.contacts.common.util.Constants;
+
+import org.json.JSONException;
+import org.json.JSONObject;
 
 /**
  * Runs ContactLoader tests for the the contact-detail and editor view.
  */
 @LargeTest
 public class ContactLoaderTest extends LoaderTestCase {
+    private static final long CONTACT_ID = 1;
+    private static final long RAW_CONTACT_ID = 11;
+    private static final long DATA_ID = 21;
+    private static final String LOOKUP_KEY = "aa%12%@!";
+
     private ContactsMockContext mMockContext;
     private MockContentProvider mContactsProvider;
 
@@ -97,27 +107,22 @@
 
     public void testLoadContactWithContactIdUri() {
         // Use content Uris that only contain the ID
-        final long contactId = 1;
-        final long rawContactId = 11;
-        final long dataId = 21;
-
-        final String lookupKey = "aa%12%@!";
-        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
         final Uri entityUri = Uri.withAppendedPath(baseUri, Contacts.Entity.CONTENT_DIRECTORY);
         final Uri lookupUri = ContentUris.withAppendedId(
-                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
-                contactId);
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
+                CONTACT_ID);
 
         ContactQueries queries = new ContactQueries();
         mContactsProvider.expectTypeQuery(baseUri, Contacts.CONTENT_ITEM_TYPE);
-        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+        queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
 
         Contact contact = assertLoadContact(baseUri);
 
-        assertEquals(contactId, contact.getId());
-        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(CONTACT_ID, contact.getId());
+        assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
-        assertEquals(lookupKey, contact.getLookupKey());
+        assertEquals(LOOKUP_KEY, contact.getLookupKey());
         assertEquals(lookupUri, contact.getLookupUri());
         assertEquals(1, contact.getRawContacts().size());
         assertEquals(1, contact.getStatuses().size());
@@ -126,30 +131,26 @@
 
     public void testLoadContactWithOldStyleUri() {
         // Use content Uris that only contain the ID but use the format used in Donut
-        final long contactId = 1;
-        final long rawContactId = 11;
-        final long dataId = 21;
-
-        final String lookupKey = "aa%12%@!";
         final Uri legacyUri = ContentUris.withAppendedId(
-                Uri.parse("content://contacts"), rawContactId);
-        final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
-        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+                Uri.parse("content://contacts"), RAW_CONTACT_ID);
+        final Uri rawContactUri = ContentUris.withAppendedId(
+                RawContacts.CONTENT_URI, RAW_CONTACT_ID);
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
         final Uri lookupUri = ContentUris.withAppendedId(
-                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
-                contactId);
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
+                CONTACT_ID);
         final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
 
         ContactQueries queries = new ContactQueries();
-        queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, contactId, lookupKey);
-        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+        queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, CONTACT_ID, LOOKUP_KEY);
+        queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
 
         Contact contact = assertLoadContact(legacyUri);
 
-        assertEquals(contactId, contact.getId());
-        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(CONTACT_ID, contact.getId());
+        assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
-        assertEquals(lookupKey, contact.getLookupKey());
+        assertEquals(LOOKUP_KEY, contact.getLookupKey());
         assertEquals(lookupUri, contact.getLookupUri());
         assertEquals(1, contact.getRawContacts().size());
         assertEquals(1, contact.getStatuses().size());
@@ -158,29 +159,25 @@
 
     public void testLoadContactWithRawContactIdUri() {
         // Use content Uris that only contain the ID but use the format used in Donut
-        final long contactId = 1;
-        final long rawContactId = 11;
-        final long dataId = 21;
-
-        final String lookupKey = "aa%12%@!";
-        final Uri rawContactUri = ContentUris.withAppendedId(RawContacts.CONTENT_URI, rawContactId);
-        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri rawContactUri = ContentUris.withAppendedId(
+                RawContacts.CONTENT_URI, RAW_CONTACT_ID);
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
         final Uri lookupUri = ContentUris.withAppendedId(
-                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
-                contactId);
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
+                CONTACT_ID);
         final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
 
         ContactQueries queries = new ContactQueries();
         mContactsProvider.expectTypeQuery(rawContactUri, RawContacts.CONTENT_ITEM_TYPE);
-        queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, contactId, lookupKey);
-        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+        queries.fetchContactIdAndLookupFromRawContactUri(rawContactUri, CONTACT_ID, LOOKUP_KEY);
+        queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
 
         Contact contact = assertLoadContact(rawContactUri);
 
-        assertEquals(contactId, contact.getId());
-        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(CONTACT_ID, contact.getId());
+        assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
-        assertEquals(lookupKey, contact.getLookupKey());
+        assertEquals(LOOKUP_KEY, contact.getLookupKey());
         assertEquals(lookupUri, contact.getLookupUri());
         assertEquals(1, contact.getRawContacts().size());
         assertEquals(1, contact.getStatuses().size());
@@ -189,27 +186,22 @@
 
     public void testLoadContactWithContactLookupUri() {
         // Use lookup-style Uris that do not contain the Contact-ID
-
-        final long contactId = 1;
-        final long rawContactId = 11;
-        final long dataId = 21;
-
-        final String lookupKey = "aa%12%@!";
-        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
-        final Uri lookupNoIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey);
-        final Uri lookupUri = ContentUris.withAppendedId(lookupNoIdUri, contactId);
-        final Uri entityUri = Uri.withAppendedPath(lookupNoIdUri, Contacts.Entity.CONTENT_DIRECTORY);
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
+        final Uri lookupNoIdUri = Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY);
+        final Uri lookupUri = ContentUris.withAppendedId(lookupNoIdUri, CONTACT_ID);
+        final Uri entityUri = Uri.withAppendedPath(
+                lookupNoIdUri, Contacts.Entity.CONTENT_DIRECTORY);
 
         ContactQueries queries = new ContactQueries();
         mContactsProvider.expectTypeQuery(lookupNoIdUri, Contacts.CONTENT_ITEM_TYPE);
-        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+        queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
 
         Contact contact = assertLoadContact(lookupNoIdUri);
 
-        assertEquals(contactId, contact.getId());
-        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(CONTACT_ID, contact.getId());
+        assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
-        assertEquals(lookupKey, contact.getLookupKey());
+        assertEquals(LOOKUP_KEY, contact.getLookupKey());
         assertEquals(lookupUri, contact.getLookupUri());
         assertEquals(1, contact.getRawContacts().size());
         assertEquals(1, contact.getStatuses().size());
@@ -218,27 +210,22 @@
 
     public void testLoadContactWithContactLookupAndIdUri() {
         // Use lookup-style Uris that also contain the Contact-ID
-        final long contactId = 1;
-        final long rawContactId = 11;
-        final long dataId = 21;
-
-        final String lookupKey = "aa%12%@!";
-        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
         final Uri lookupUri = ContentUris.withAppendedId(
-                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
-                contactId);
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
+                CONTACT_ID);
         final Uri entityUri = Uri.withAppendedPath(lookupUri, Contacts.Entity.CONTENT_DIRECTORY);
 
         ContactQueries queries = new ContactQueries();
         mContactsProvider.expectTypeQuery(lookupUri, Contacts.CONTENT_ITEM_TYPE);
-        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+        queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
 
         Contact contact = assertLoadContact(lookupUri);
 
-        assertEquals(contactId, contact.getId());
-        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(CONTACT_ID, contact.getId());
+        assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
-        assertEquals(lookupKey, contact.getLookupKey());
+        assertEquals(LOOKUP_KEY, contact.getLookupKey());
         assertEquals(lookupUri, contact.getLookupUri());
         assertEquals(1, contact.getRawContacts().size());
         assertEquals(1, contact.getStatuses().size());
@@ -248,36 +235,31 @@
     public void testLoadContactWithContactLookupWithIncorrectIdUri() {
         // Use lookup-style Uris that contain incorrect Contact-ID
         // (we want to ensure that still the correct contact is chosen)
-
-        final long contactId = 1;
         final long wrongContactId = 2;
-        final long rawContactId = 11;
         final long wrongRawContactId = 12;
-        final long dataId = 21;
 
-        final String lookupKey = "aa%12%@!";
         final String wrongLookupKey = "ab%12%@!";
-        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, contactId);
+        final Uri baseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, CONTACT_ID);
         final Uri wrongBaseUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, wrongContactId);
         final Uri lookupUri = ContentUris.withAppendedId(
-                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
-                contactId);
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
+                CONTACT_ID);
         final Uri lookupWithWrongIdUri = ContentUris.withAppendedId(
-                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, lookupKey),
+                Uri.withAppendedPath(Contacts.CONTENT_LOOKUP_URI, LOOKUP_KEY),
                 wrongContactId);
         final Uri entityUri = Uri.withAppendedPath(lookupWithWrongIdUri,
                 Contacts.Entity.CONTENT_DIRECTORY);
 
         ContactQueries queries = new ContactQueries();
         mContactsProvider.expectTypeQuery(lookupWithWrongIdUri, Contacts.CONTENT_ITEM_TYPE);
-        queries.fetchAllData(entityUri, contactId, rawContactId, dataId, lookupKey);
+        queries.fetchAllData(entityUri, CONTACT_ID, RAW_CONTACT_ID, DATA_ID, LOOKUP_KEY);
 
         Contact contact = assertLoadContact(lookupWithWrongIdUri);
 
-        assertEquals(contactId, contact.getId());
-        assertEquals(rawContactId, contact.getNameRawContactId());
+        assertEquals(CONTACT_ID, contact.getId());
+        assertEquals(RAW_CONTACT_ID, contact.getNameRawContactId());
         assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
-        assertEquals(lookupKey, contact.getLookupKey());
+        assertEquals(LOOKUP_KEY, contact.getLookupKey());
         assertEquals(lookupUri, contact.getLookupUri());
         assertEquals(1, contact.getRawContacts().size());
         assertEquals(1, contact.getStatuses().size());
@@ -285,6 +267,37 @@
         mContactsProvider.verify();
     }
 
+    public void testLoadContactReturnDirectoryContactWithoutDisplayName() throws JSONException {
+        // Use lookup-style Uri that contains encoded json object which encapsulates the
+        // directory contact. The test json object is:
+        // {
+        //   display_name_source": 40,
+        //   "vnd.android.cursor.item\/contact":{"email":{"data1":"test@google.com" }}
+        // }
+        JSONObject itemJson = new JSONObject();
+        itemJson.put("email", new JSONObject().put("data1", "test@google.com"));
+        JSONObject json = new JSONObject();
+        json.put(Contacts.NAME_RAW_CONTACT_ID, CONTACT_ID);
+        json.put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME);
+        json.put(Contacts.CONTENT_ITEM_TYPE, itemJson);
+
+        final Uri lookupUri = Contacts.CONTENT_LOOKUP_URI.buildUpon()
+                .encodedFragment(json.toString())
+                .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY, "1")
+                .appendPath(Constants.LOOKUP_URI_ENCODED).build();
+
+        mContactsProvider.expectTypeQuery(lookupUri, Contacts.CONTENT_ITEM_TYPE);
+        Contact contact = assertLoadContact(lookupUri);
+
+        assertEquals(-1, contact.getId());
+        assertEquals(-1, contact.getNameRawContactId());
+        assertEquals(DisplayNameSources.STRUCTURED_NAME, contact.getDisplayNameSource());
+        assertEquals("", contact.getDisplayName());
+        assertEquals(lookupUri, contact.getLookupUri());
+        assertEquals(1, contact.getRawContacts().size());
+        mContactsProvider.verify();
+    }
+
     class ContactQueries {
         public void fetchAllData(
                 Uri baseUri, long contactId, long rawContactId, long dataId, String encodedLookup) {