Merge changes Ib92b055d,I5c9f66ff

* changes:
  Update SearchAdapter query if fragment has query before fragment is ready.
  Contacts are now searchable by company name.
diff --git a/java/com/android/dialer/searchfragment/common/Projections.java b/java/com/android/dialer/searchfragment/common/Projections.java
index 078c3e5..aaf9e80 100644
--- a/java/com/android/dialer/searchfragment/common/Projections.java
+++ b/java/com/android/dialer/searchfragment/common/Projections.java
@@ -16,37 +16,47 @@
 
 package com.android.dialer.searchfragment.common;
 
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Data;
 
 /** Class containing relevant projections for searching contacts. */
 public class Projections {
 
-  public static final int PHONE_ID = 0;
+  public static final int ID = 0;
   public static final int PHONE_TYPE = 1;
   public static final int PHONE_LABEL = 2;
   public static final int PHONE_NUMBER = 3;
-  public static final int PHONE_DISPLAY_NAME = 4;
-  public static final int PHONE_PHOTO_ID = 5;
-  public static final int PHONE_PHOTO_URI = 6;
-  public static final int PHONE_LOOKUP_KEY = 7;
-  public static final int PHONE_CARRIER_PRESENCE = 8;
-  public static final int PHONE_CONTACT_ID = 9;
+  public static final int DISPLAY_NAME = 4;
+  public static final int PHOTO_ID = 5;
+  public static final int PHOTO_URI = 6;
+  public static final int LOOKUP_KEY = 7;
+  public static final int CARRIER_PRESENCE = 8;
+  public static final int CONTACT_ID = 9;
+  public static final int MIME_TYPE = 10;
 
   @SuppressWarnings("unused")
-  public static final int PHONE_SORT_KEY = 10;
+  public static final int SORT_KEY = 11;
 
-  public static final String[] PHONE_PROJECTION =
+  public static final int COMPANY_NAME = 12;
+  public static final int NICKNAME = 13;
+
+  public static final String[] DATA_PROJECTION =
       new String[] {
-        Phone._ID, // 0
+        Data._ID, // 0
         Phone.TYPE, // 1
         Phone.LABEL, // 2
         Phone.NUMBER, // 3
-        Phone.DISPLAY_NAME_PRIMARY, // 4
-        Phone.PHOTO_ID, // 5
-        Phone.PHOTO_THUMBNAIL_URI, // 6
-        Phone.LOOKUP_KEY, // 7
-        Phone.CARRIER_PRESENCE, // 8
-        Phone.CONTACT_ID, // 9
-        Phone.SORT_KEY_PRIMARY // 10
+        Data.DISPLAY_NAME_PRIMARY, // 4
+        Data.PHOTO_ID, // 5
+        Data.PHOTO_THUMBNAIL_URI, // 6
+        Data.LOOKUP_KEY, // 7
+        Data.CARRIER_PRESENCE, // 8
+        Data.CONTACT_ID, // 9
+        Data.MIMETYPE, // 10
+        Data.SORT_KEY_PRIMARY, // 11
+        Organization.COMPANY, // 12
+        Nickname.NAME // 13
       };
 }
diff --git a/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java b/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
index 775f8de..6b5cea8 100644
--- a/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
+++ b/java/com/android/dialer/searchfragment/common/QueryFilteringUtil.java
@@ -81,6 +81,10 @@
    * </ul>
    */
   public static boolean nameContainsQuery(String query, String name) {
+    if (TextUtils.isEmpty(name)) {
+      return false;
+    }
+
     return Pattern.compile("(^|\\s)" + Pattern.quote(query.toLowerCase()))
         .matcher(name.toLowerCase())
         .find();
diff --git a/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java b/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
index 6fd053c..9a0ca00 100644
--- a/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
+++ b/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java
@@ -21,17 +21,25 @@
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.DataSetObserver;
+import android.database.MatrixCursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
 import android.support.annotation.IntDef;
 import android.support.annotation.Nullable;
+import android.support.v4.util.ArraySet;
 import android.text.TextUtils;
+import com.android.dialer.common.Assert;
 import com.android.dialer.searchfragment.common.Projections;
 import com.android.dialer.searchfragment.common.QueryFilteringUtil;
 import java.lang.annotation.Retention;
 import java.lang.annotation.RetentionPolicy;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.List;
+import java.util.Set;
 
 /**
  * Wrapper for a cursor containing all on device contacts.
@@ -63,92 +71,109 @@
   }
 
   /**
-   * @param cursor with projection {@link Projections#PHONE_PROJECTION}.
+   * @param cursor with projection {@link Projections#DATA_PROJECTION}.
    * @param query to filter cursor results.
    */
   ContactFilterCursor(Cursor cursor, @Nullable String query) {
-    // TODO(calderwoodra) investigate copying this into a MatrixCursor and holding in memory
-    this.cursor = cursor;
+    this.cursor = createCursor(cursor);
     filter(query);
   }
 
   /**
-   * Filters out contacts that do not match the query.
+   * Returns a new cursor with contact information coalesced.
    *
-   * <p>The query can have at least 1 of 3 forms:
+   * <p>Here are some sample rows and columns that might exist in cp2 database:
    *
    * <ul>
-   *   <li>A phone number
-   *   <li>A T9 representation of a name (matches {@link QueryFilteringUtil#T9_PATTERN}).
-   *   <li>A name
+   *   <li>display Name (William), contactID (202), mimeType (name), data1 (A Pixel)
+   *   <li>display Name (William), contactID (202), mimeType (phone), data1 (+1 650-200-3333)
+   *   <li>display Name (William), contactID (202), mimeType (phone), data1 (+1 540-555-6666)
+   *   <li>display Name (William), contactID (202), mimeType (organization), data1 (Walmart)
+   *   <li>display Name (William), contactID (202), mimeType (nickname), data1 (Will)
    * </ul>
    *
-   * <p>A contact is considered a match if:
+   * <p>These rows would be coalesced into new rows like so:
    *
    * <ul>
-   *   <li>Its phone number contains the phone number query
-   *   <li>Its name represented in T9 contains the T9 query
-   *   <li>Its name contains the query
+   *   <li>display Name (William), phoneNumber (+1 650-200-3333), organization (Walmart), nickname
+   *       (Will)
+   *   <li>display Name (William), phoneNumber (+1 540-555-6666), organization (Walmart), nickname
+   *       (Will)
    * </ul>
    */
-  public void filter(@Nullable String query) {
-    if (query == null) {
-      query = "";
-    }
-    queryFilteredPositions.clear();
-
-    // On some devices, contacts have multiple rows with identical phone numbers. These numbers are
-    // considered duplicates. Since the order might not be guaranteed, we compare all of the numbers
-    // and hold onto the most qualified one as the one we want to display to the user.
-    // See #getQualification for details on how qualification is determined.
-    int previousMostQualifiedPosition = 0;
-    String previousName = "";
-    String previousMostQualifiedNumber = "";
-
-    query = query.toLowerCase();
+  private static Cursor createCursor(Cursor cursor) {
+    // Convert cursor rows into Cp2Contacts
+    List<Cp2Contact> cp2Contacts = new ArrayList<>();
+    Set<Integer> contactIds = new ArraySet<>();
     cursor.moveToPosition(-1);
-
     while (cursor.moveToNext()) {
-      int position = cursor.getPosition();
-      String currentNumber = cursor.getString(Projections.PHONE_NUMBER);
-      String currentName = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+      Cp2Contact contact = Cp2Contact.fromCursor(cursor);
+      cp2Contacts.add(contact);
+      contactIds.add(contact.contactId());
+    }
+    cursor.close();
 
-      if (!previousName.equals(currentName)) {
-        previousName = currentName;
-        previousMostQualifiedNumber = currentNumber;
-        previousMostQualifiedPosition = position;
-      } else {
-        // Since the contact name is the same, check if this number is a duplicate
-        switch (getQualification(currentNumber, previousMostQualifiedNumber)) {
-          case Qualification.CURRENT_MORE_QUALIFIED:
-            // Number is a less qualified duplicate, ignore it.
-            continue;
-          case Qualification.NEW_NUMBER_IS_MORE_QUALIFIED:
-            // If number wasn't filtered out before, remove it and add it's more qualified version.
-            int index = queryFilteredPositions.indexOf(previousMostQualifiedPosition);
-            if (index != -1) {
-              queryFilteredPositions.remove(index);
-              queryFilteredPositions.add(position);
-            }
-            previousMostQualifiedNumber = currentNumber;
-            previousMostQualifiedPosition = position;
-            continue;
-          case Qualification.NUMBERS_ARE_NOT_DUPLICATES:
-          default:
-            previousMostQualifiedNumber = currentNumber;
-            previousMostQualifiedPosition = position;
+    // Group then combine contact data
+    List<Cp2Contact> coalescedContacts = new ArrayList<>();
+    for (Integer contactId : contactIds) {
+      List<Cp2Contact> duplicateContacts = getAllContactsWithContactId(contactId, cp2Contacts);
+      coalescedContacts.addAll(coalesceContacts(duplicateContacts));
+    }
+
+    // Sort by display name, then build new cursor from coalesced contacts.
+    // We sort the contacts so that they are displayed to the user in lexicographic order.
+    Collections.sort(coalescedContacts, (o1, o2) -> o1.displayName().compareTo(o2.displayName()));
+    MatrixCursor newCursor =
+        new MatrixCursor(Projections.DATA_PROJECTION, coalescedContacts.size());
+    for (Cp2Contact contact : coalescedContacts) {
+      newCursor.addRow(contact.toCursorRow());
+    }
+    return newCursor;
+  }
+
+  private static List<Cp2Contact> coalesceContacts(List<Cp2Contact> contactsWithSameContactId) {
+    String companyName = null;
+    String nickName = null;
+    List<Cp2Contact> phoneContacts = new ArrayList<>();
+    for (Cp2Contact contact : contactsWithSameContactId) {
+      if (contact.mimeType().equals(Phone.CONTENT_ITEM_TYPE)) {
+        phoneContacts.add(contact);
+      } else if (contact.mimeType().equals(Organization.CONTENT_ITEM_TYPE)) {
+        Assert.checkArgument(TextUtils.isEmpty(companyName));
+        companyName = contact.companyName();
+      } else if (contact.mimeType().equals(Nickname.CONTENT_ITEM_TYPE)) {
+        Assert.checkArgument(TextUtils.isEmpty(nickName));
+        nickName = contact.nickName();
+      }
+    }
+
+    removeDuplicatePhoneNumbers(phoneContacts);
+
+    List<Cp2Contact> coalescedContacts = new ArrayList<>();
+    for (Cp2Contact phoneContact : phoneContacts) {
+      coalescedContacts.add(
+          phoneContact.toBuilder().setCompanyName(companyName).setNickName(nickName).build());
+    }
+    return coalescedContacts;
+  }
+
+  private static void removeDuplicatePhoneNumbers(List<Cp2Contact> phoneContacts) {
+    for (int i = 0; i < phoneContacts.size(); i++) {
+      Cp2Contact contact1 = phoneContacts.get(i);
+      for (int j = i + 1; j < phoneContacts.size(); /* don't iterate by default */ ) {
+        Cp2Contact contact2 = phoneContacts.get(j);
+        int qualification = getQualification(contact2.phoneNumber(), contact1.phoneNumber());
+        if (qualification == Qualification.CURRENT_MORE_QUALIFIED) {
+          phoneContacts.remove(contact2);
+        } else if (qualification == Qualification.NEW_NUMBER_IS_MORE_QUALIFIED) {
+          phoneContacts.remove(contact1);
+          break;
+        } else if (qualification == Qualification.NUMBERS_ARE_NOT_DUPLICATES) {
+          // Keep both contacts
+          j++;
         }
       }
-
-      if (TextUtils.isEmpty(query)
-          || QueryFilteringUtil.nameMatchesT9Query(query, previousName)
-          || QueryFilteringUtil.numberMatchesNumberQuery(query, previousMostQualifiedNumber)
-          || QueryFilteringUtil.nameContainsQuery(query, previousName)) {
-        queryFilteredPositions.add(previousMostQualifiedPosition);
-      }
     }
-    currentPosition = 0;
-    cursor.moveToFirst();
   }
 
   /**
@@ -157,7 +182,7 @@
    * @return {@link Qualification} where the more qualified number is the number with the most
    *     digits. If the digits are the same, the number with the most formatting is more qualified.
    */
-  private @Qualification int getQualification(String number, String mostQualifiedNumber) {
+  private static @Qualification int getQualification(String number, String mostQualifiedNumber) {
     // Ignore formatting
     String numberDigits = QueryFilteringUtil.digitsOnly(number);
     String qualifiedNumberDigits = QueryFilteringUtil.digitsOnly(mostQualifiedNumber);
@@ -182,6 +207,64 @@
     return Qualification.NUMBERS_ARE_NOT_DUPLICATES;
   }
 
+  private static List<Cp2Contact> getAllContactsWithContactId(
+      int contactId, List<Cp2Contact> contacts) {
+    List<Cp2Contact> contactIdContacts = new ArrayList<>();
+    for (Cp2Contact contact : contacts) {
+      if (contact.contactId() == contactId) {
+        contactIdContacts.add(contact);
+      }
+    }
+    return contactIdContacts;
+  }
+
+  /**
+   * Filters out contacts that do not match the query.
+   *
+   * <p>The query can have at least 1 of 3 forms:
+   *
+   * <ul>
+   *   <li>A phone number
+   *   <li>A T9 representation of a name (matches {@link QueryFilteringUtil#T9_PATTERN}).
+   *   <li>A name
+   * </ul>
+   *
+   * <p>A contact is considered a match if:
+   *
+   * <ul>
+   *   <li>Its phone number contains the phone number query
+   *   <li>Its name represented in T9 contains the T9 query
+   *   <li>Its name contains the query
+   *   <li>Its company contains the query
+   * </ul>
+   */
+  public void filter(@Nullable String query) {
+    if (query == null) {
+      query = "";
+    }
+    queryFilteredPositions.clear();
+    query = query.toLowerCase();
+    cursor.moveToPosition(-1);
+
+    while (cursor.moveToNext()) {
+      int position = cursor.getPosition();
+      String number = cursor.getString(Projections.PHONE_NUMBER);
+      String name = cursor.getString(Projections.DISPLAY_NAME);
+      String companyName = cursor.getString(Projections.COMPANY_NAME);
+      String nickName = cursor.getString(Projections.NICKNAME);
+      if (TextUtils.isEmpty(query)
+          || QueryFilteringUtil.nameMatchesT9Query(query, name)
+          || QueryFilteringUtil.numberMatchesNumberQuery(query, number)
+          || QueryFilteringUtil.nameContainsQuery(query, name)
+          || QueryFilteringUtil.nameContainsQuery(query, companyName)
+          || QueryFilteringUtil.nameContainsQuery(query, nickName)) {
+        queryFilteredPositions.add(position);
+      }
+    }
+    currentPosition = 0;
+    cursor.moveToFirst();
+  }
+
   @Override
   public boolean moveToPosition(int position) {
     currentPosition = position;
diff --git a/java/com/android/dialer/searchfragment/cp2/Cp2Contact.java b/java/com/android/dialer/searchfragment/cp2/Cp2Contact.java
new file mode 100644
index 0000000..f199f67
--- /dev/null
+++ b/java/com/android/dialer/searchfragment/cp2/Cp2Contact.java
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2017 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
+ */
+
+package com.android.dialer.searchfragment.cp2;
+
+import android.database.Cursor;
+import android.support.annotation.Nullable;
+import com.android.dialer.searchfragment.common.Projections;
+import com.google.auto.value.AutoValue;
+
+/** POJO Representation for contacts returned in {@link SearchContactsCursorLoader}. */
+@AutoValue
+public abstract class Cp2Contact {
+
+  public abstract long phoneId();
+
+  public abstract int phoneType();
+
+  @Nullable
+  public abstract String phoneLabel();
+
+  public abstract String phoneNumber();
+
+  @Nullable
+  public abstract String displayName();
+
+  public abstract int photoId();
+
+  @Nullable
+  public abstract String photoUri();
+
+  public abstract String lookupKey();
+
+  public abstract int carrierPresence();
+
+  public abstract int contactId();
+
+  @Nullable
+  public abstract String companyName();
+
+  @Nullable
+  public abstract String nickName();
+
+  public abstract String mimeType();
+
+  /** Builder for {@link Cp2Contact}. */
+  @AutoValue.Builder
+  public abstract static class Builder {
+    public abstract Builder setPhoneId(long id);
+
+    public abstract Builder setPhoneType(int type);
+
+    public abstract Builder setPhoneLabel(@Nullable String label);
+
+    public abstract Builder setPhoneNumber(String number);
+
+    public abstract Builder setDisplayName(@Nullable String name);
+
+    public abstract Builder setPhotoId(int id);
+
+    public abstract Builder setPhotoUri(@Nullable String uri);
+
+    public abstract Builder setLookupKey(String lookupKey);
+
+    public abstract Builder setCarrierPresence(int presence);
+
+    public abstract Builder setContactId(int id);
+
+    public abstract Builder setCompanyName(@Nullable String name);
+
+    public abstract Builder setNickName(@Nullable String nickName);
+
+    public abstract Builder setMimeType(String mimeType);
+
+    public abstract Cp2Contact build();
+  }
+
+  public static Builder builder() {
+    return new AutoValue_Cp2Contact.Builder();
+  }
+
+  public static Cp2Contact fromCursor(Cursor cursor) {
+    return Cp2Contact.builder()
+        .setPhoneId(cursor.getLong(Projections.CONTACT_ID))
+        .setPhoneType(cursor.getInt(Projections.PHONE_TYPE))
+        .setPhoneLabel(cursor.getString(Projections.PHONE_LABEL))
+        .setPhoneNumber(cursor.getString(Projections.PHONE_NUMBER))
+        .setDisplayName(cursor.getString(Projections.DISPLAY_NAME))
+        .setPhotoId(cursor.getInt(Projections.PHOTO_ID))
+        .setPhotoUri(cursor.getString(Projections.PHOTO_URI))
+        .setLookupKey(cursor.getString(Projections.LOOKUP_KEY))
+        .setCarrierPresence(cursor.getInt(Projections.CARRIER_PRESENCE))
+        .setContactId(cursor.getInt(Projections.CONTACT_ID))
+        .setCompanyName(cursor.getString(Projections.COMPANY_NAME))
+        .setNickName(cursor.getString(Projections.NICKNAME))
+        .setMimeType(cursor.getString(Projections.MIME_TYPE))
+        .build();
+  }
+
+  public Object[] toCursorRow() {
+    Object[] row = new Object[Projections.DATA_PROJECTION.length];
+    row[Projections.ID] = phoneId();
+    row[Projections.PHONE_TYPE] = phoneType();
+    row[Projections.PHONE_LABEL] = phoneLabel();
+    row[Projections.PHONE_NUMBER] = phoneNumber();
+    row[Projections.DISPLAY_NAME] = displayName();
+    row[Projections.PHOTO_ID] = photoId();
+    row[Projections.PHOTO_URI] = photoUri();
+    row[Projections.LOOKUP_KEY] = lookupKey();
+    row[Projections.CARRIER_PRESENCE] = carrierPresence();
+    row[Projections.CONTACT_ID] = contactId();
+    row[Projections.COMPANY_NAME] = companyName();
+    row[Projections.NICKNAME] = nickName();
+    row[Projections.MIME_TYPE] = mimeType();
+    return row;
+  }
+
+  public abstract Builder toBuilder();
+}
diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java b/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
index b162a5e..c09396c 100644
--- a/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
+++ b/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java
@@ -96,7 +96,7 @@
     dialerContact = getDialerContact(context, cursor);
     position = cursor.getPosition();
     number = cursor.getString(Projections.PHONE_NUMBER);
-    String name = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+    String name = cursor.getString(Projections.DISPLAY_NAME);
     String label = getLabel(context.getResources(), cursor);
     String secondaryInfo =
         TextUtils.isEmpty(label)
@@ -111,12 +111,12 @@
     if (shouldShowPhoto(cursor)) {
       nameOrNumberView.setVisibility(View.VISIBLE);
       photo.setVisibility(View.VISIBLE);
-      String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
+      String photoUri = cursor.getString(Projections.PHOTO_URI);
       ContactPhotoManager.getInstance(context)
           .loadDialerThumbnailOrPhoto(
               photo,
               getContactUri(cursor),
-              cursor.getLong(Projections.PHONE_PHOTO_ID),
+              cursor.getLong(Projections.PHOTO_ID),
               photoUri == null ? null : Uri.parse(photoUri),
               name,
               LetterTileDrawable.TYPE_DEFAULT);
@@ -129,11 +129,11 @@
   // Show the contact photo next to only the first number if a contact has multiple numbers
   private boolean shouldShowPhoto(SearchCursor cursor) {
     int currentPosition = cursor.getPosition();
-    String currentLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+    String currentLookupKey = cursor.getString(Projections.LOOKUP_KEY);
     cursor.moveToPosition(currentPosition - 1);
 
     if (!cursor.isHeader() && !cursor.isBeforeFirst()) {
-      String previousLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+      String previousLookupKey = cursor.getString(Projections.LOOKUP_KEY);
       cursor.moveToPosition(currentPosition);
       return !currentLookupKey.equals(previousLookupKey);
     }
@@ -142,8 +142,8 @@
   }
 
   private static Uri getContactUri(Cursor cursor) {
-    long contactId = cursor.getLong(Projections.PHONE_ID);
-    String lookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+    long contactId = cursor.getLong(Projections.ID);
+    String lookupKey = cursor.getString(Projections.LOOKUP_KEY);
     return Contacts.getLookupUri(contactId, lookupKey);
   }
 
@@ -188,7 +188,7 @@
 
   private static @CallToAction int getCallToAction(
       Context context, SearchCursor cursor, String query) {
-    int carrierPresence = cursor.getInt(Projections.PHONE_CARRIER_PRESENCE);
+    int carrierPresence = cursor.getInt(Projections.CARRIER_PRESENCE);
     String number = cursor.getString(Projections.PHONE_NUMBER);
     if ((carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) == 1) {
       return CallToAction.VIDEO_CALL;
@@ -262,16 +262,15 @@
 
   private static DialerContact getDialerContact(Context context, Cursor cursor) {
     DialerContact.Builder contact = DialerContact.newBuilder();
-    String displayName = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+    String displayName = cursor.getString(Projections.DISPLAY_NAME);
     String number = cursor.getString(Projections.PHONE_NUMBER);
     Uri contactUri =
         Contacts.getLookupUri(
-            cursor.getLong(Projections.PHONE_CONTACT_ID),
-            cursor.getString(Projections.PHONE_LOOKUP_KEY));
+            cursor.getLong(Projections.CONTACT_ID), cursor.getString(Projections.LOOKUP_KEY));
 
     contact
         .setNumber(number)
-        .setPhotoId(cursor.getLong(Projections.PHONE_PHOTO_ID))
+        .setPhotoId(cursor.getLong(Projections.PHOTO_ID))
         .setContactType(LetterTileDrawable.TYPE_DEFAULT)
         .setNameOrNumber(displayName)
         .setNumberLabel(
@@ -281,7 +280,7 @@
                     cursor.getString(Projections.PHONE_LABEL))
                 .toString());
 
-    String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
+    String photoUri = cursor.getString(Projections.PHOTO_URI);
     if (photoUri != null) {
       contact.setPhotoUri(photoUri);
     }
diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
index b7fc9b5..f1230c6 100644
--- a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java
@@ -19,7 +19,10 @@
 import android.content.Context;
 import android.content.CursorLoader;
 import android.database.Cursor;
+import android.provider.ContactsContract.CommonDataKinds.Nickname;
+import android.provider.ContactsContract.CommonDataKinds.Organization;
 import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.Data;
 import android.support.annotation.Nullable;
 import com.android.dialer.searchfragment.common.Projections;
 
@@ -32,14 +35,27 @@
   public SearchContactsCursorLoader(Context context, @Nullable String query) {
     super(
         context,
-        Phone.CONTENT_URI,
-        Projections.PHONE_PROJECTION,
-        null,
+        Data.CONTENT_URI,
+        Projections.DATA_PROJECTION,
+        whereStatement(),
         null,
         Phone.SORT_KEY_PRIMARY + " ASC");
     this.query = query;
   }
 
+  private static String whereStatement() {
+    return (Phone.NUMBER + " IS NOT NULL")
+        + " AND "
+        + Data.MIMETYPE
+        + " IN (\'"
+        + Phone.CONTENT_ITEM_TYPE
+        + "\', \'"
+        + Nickname.CONTENT_ITEM_TYPE
+        + "\', \'"
+        + Organization.CONTENT_ITEM_TYPE
+        + "\')";
+  }
+
   @Override
   public Cursor loadInBackground() {
     // All contacts
diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
index 6115c2f..2544535 100644
--- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
+++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java
@@ -125,6 +125,7 @@
       LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle savedInstanceState) {
     View view = inflater.inflate(R.layout.fragment_search, parent, false);
     adapter = new SearchAdapter(getActivity(), new SearchCursorManager());
+    adapter.setQuery(query);
     adapter.setCallInitiationType(callInitiationType);
     adapter.setSearchActions(getActions());
     adapter.setZeroSuggestVisible(getArguments().getBoolean(KEY_SHOW_ZERO_SUGGEST));
diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
index fa07826..5d51880 100644
--- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
+++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlaceViewHolder.java
@@ -61,17 +61,17 @@
    */
   public void bind(SearchCursor cursor, String query) {
     number = cursor.getString(Projections.PHONE_NUMBER);
-    String name = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+    String name = cursor.getString(Projections.DISPLAY_NAME);
     String address = cursor.getString(Projections.PHONE_LABEL);
 
     placeName.setText(QueryBoldingUtil.getNameWithQueryBolded(query, name));
     placeAddress.setText(QueryBoldingUtil.getNameWithQueryBolded(query, address));
-    String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
+    String photoUri = cursor.getString(Projections.PHOTO_URI);
     ContactPhotoManager.getInstance(context)
         .loadDialerThumbnailOrPhoto(
             photo,
             getContactUri(cursor),
-            cursor.getLong(Projections.PHONE_PHOTO_ID),
+            cursor.getLong(Projections.PHOTO_ID),
             photoUri == null ? null : Uri.parse(photoUri),
             name,
             LetterTileDrawable.TYPE_BUSINESS);
@@ -81,7 +81,7 @@
     // Since the lookup key for Nearby Places is actually a JSON representation of the information,
     // we need to pass it in as an encoded fragment in our contact uri.
     // It includes information like display name, photo uri, phone number, ect.
-    String businessInfoJson = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+    String businessInfoJson = cursor.getString(Projections.LOOKUP_KEY);
     return Contacts.CONTENT_LOOKUP_URI
         .buildUpon()
         .appendPath(Constants.LOOKUP_URI_ENCODED)
diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
index c8bb36a..0d52c10 100644
--- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java
@@ -41,7 +41,7 @@
    */
   public NearbyPlacesCursorLoader(
       Context context, String query, @NonNull List<Integer> directoryIds) {
-    super(context, getContentUri(context, query), Projections.PHONE_PROJECTION, null, null, null);
+    super(context, getContentUri(context, query), Projections.DATA_PROJECTION, null, null, null);
     this.directoryId = getDirectoryId(directoryIds);
   }
 
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java b/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
index df3eacc..8a02eb9 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactViewHolder.java
@@ -64,7 +64,7 @@
    */
   public void bind(SearchCursor cursor, String query) {
     number = cursor.getString(Projections.PHONE_NUMBER);
-    String name = cursor.getString(Projections.PHONE_DISPLAY_NAME);
+    String name = cursor.getString(Projections.DISPLAY_NAME);
     String label = getLabel(context.getResources(), cursor);
     String secondaryInfo =
         TextUtils.isEmpty(label)
@@ -78,12 +78,12 @@
     if (shouldShowPhoto(cursor)) {
       nameView.setVisibility(View.VISIBLE);
       photo.setVisibility(View.VISIBLE);
-      String photoUri = cursor.getString(Projections.PHONE_PHOTO_URI);
+      String photoUri = cursor.getString(Projections.PHOTO_URI);
       ContactPhotoManager.getInstance(context)
           .loadDialerThumbnailOrPhoto(
               photo,
               getContactUri(cursor),
-              cursor.getLong(Projections.PHONE_PHOTO_ID),
+              cursor.getLong(Projections.PHOTO_ID),
               photoUri == null ? null : Uri.parse(photoUri),
               name,
               LetterTileDrawable.TYPE_DEFAULT);
@@ -96,11 +96,11 @@
   // Show the contact photo next to only the first number if a contact has multiple numbers
   private boolean shouldShowPhoto(SearchCursor cursor) {
     int currentPosition = cursor.getPosition();
-    String currentLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+    String currentLookupKey = cursor.getString(Projections.LOOKUP_KEY);
     cursor.moveToPosition(currentPosition - 1);
 
     if (!cursor.isHeader() && !cursor.isBeforeFirst()) {
-      String previousLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+      String previousLookupKey = cursor.getString(Projections.LOOKUP_KEY);
       cursor.moveToPosition(currentPosition);
       return !currentLookupKey.equals(previousLookupKey);
     }
@@ -121,8 +121,8 @@
   }
 
   private static Uri getContactUri(SearchCursor cursor) {
-    long contactId = cursor.getLong(Projections.PHONE_ID);
-    String lookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY);
+    long contactId = cursor.getLong(Projections.ID);
+    String lookupKey = cursor.getString(Projections.LOOKUP_KEY);
     return Contacts.getLookupUri(contactId, lookupKey)
         .buildUpon()
         .appendQueryParameter(
diff --git a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
index 37695be..eb47273 100644
--- a/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
+++ b/java/com/android/dialer/searchfragment/remote/RemoteContactsCursorLoader.java
@@ -54,7 +54,7 @@
     super(
         context,
         null,
-        Projections.PHONE_PROJECTION,
+        Projections.DATA_PROJECTION,
         IGNORE_NUMBER_TOO_LONG_CLAUSE,
         null,
         Phone.SORT_KEY_PRIMARY);
diff --git a/java/com/android/dialer/searchfragment/testing/TestCursorSchema.java b/java/com/android/dialer/searchfragment/testing/TestCursorSchema.java
new file mode 100644
index 0000000..375fdb5
--- /dev/null
+++ b/java/com/android/dialer/searchfragment/testing/TestCursorSchema.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2017 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
+ */
+
+package com.android.dialer.searchfragment.testing;
+
+import android.provider.ContactsContract.Data;
+
+/** Testing class containing cp2 cursor testing utilities. */
+public final class TestCursorSchema {
+
+  /**
+   * If new rows are added to {@link
+   * com.android.dialer.searchfragment.common.Projections#DATA_PROJECTION}, this schema should be
+   * updated.
+   */
+  // TODO(67909522): remove these extra columns and remove all references to "Phone."
+  public static final String[] SCHEMA =
+      new String[] {
+        Data._ID, // 0
+        "data2", // 1 Phone Type
+        "data3", // 2 Phone Label
+        "data1", // 3 Phone Number, Organization Company
+        Data.DISPLAY_NAME_PRIMARY, // 4
+        Data.PHOTO_ID, // 5
+        Data.PHOTO_THUMBNAIL_URI, // 6
+        Data.LOOKUP_KEY, // 7
+        Data.CARRIER_PRESENCE, // 8
+        Data.CONTACT_ID, // 9
+        Data.MIMETYPE, // 10
+        Data.SORT_KEY_PRIMARY, // 11
+        "company_name", // 12, no constant because Organization.COMPANY.equals("data1")
+        "nick_name" // 13, no constant because Nickname.NAME.equals("data1")
+      };
+}