Disable search result if no contact detail to show.

Fixes: 147157426
Test: manually
Change-Id: I68f7a645fd22c3f4602ca46ed3047336949b84bb
diff --git a/res/layout/contact_result.xml b/res/layout/contact_result.xml
index f6188f7..d89b29f 100644
--- a/res/layout/contact_result.xml
+++ b/res/layout/contact_result.xml
@@ -39,6 +39,7 @@
         android:layout_height="wrap_content"
         android:singleLine="true"
         android:textAppearance="@style/TextAppearance.ContactResultTitle"
+        android:duplicateParentState="true"
         app:layout_constraintTop_toTopOf="parent"
         app:layout_constraintBottom_toBottomOf="parent"
         app:layout_constraintStart_toStartOf="parent"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 0418d13..a8f1ab1 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -150,6 +150,9 @@
         " - Favorite"
     </string>
 
+    <!-- Message in the add-to-favorite flow that no phone number available for this contact [CHAR_LIMIT=50] -->
+    <string name="no_phone_numbers">No phone numbers</string>
+
     <!-- Heads Up Notification -->
     <!-- Name of incoming call notification channel in app info [CHAR LIMIT=50] -->
     <string name="in_call_notification_channel_name">Incoming call notification</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index af01627..a7c24a3 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -106,7 +106,9 @@
     </style>
 
     <!-- Contact results -->
-    <style name="TextAppearance.ContactResultTitle" parent="TextAppearance.Body1"/>
+    <style name="TextAppearance.ContactResultTitle" parent="TextAppearance.Body1">
+        <item name="android:textColor">@color/primary_text_selector</item>
+    </style>
 
     <!-- Add to favorite flow dialog -->
     <style name="TextAppearance.AddFavoriteNumberTitle" parent="TextAppearance.Body1">
diff --git a/src/com/android/car/dialer/ui/common/DialerUtils.java b/src/com/android/car/dialer/ui/common/DialerUtils.java
index 3bcca38..01e6931 100644
--- a/src/com/android/car/dialer/ui/common/DialerUtils.java
+++ b/src/com/android/car/dialer/ui/common/DialerUtils.java
@@ -17,6 +17,10 @@
 package com.android.car.dialer.ui.common;
 
 import android.content.Context;
+import android.content.res.Resources;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
 
 import com.android.car.dialer.R;
 import com.android.car.dialer.log.L;
@@ -106,4 +110,23 @@
             L.w(TAG, "contact %s doesn't have any phone number", contact.getDisplayName());
         }
     }
+
+    /**
+     * Returns true if the contact has postal address and show postal address config is true.
+     */
+    private static boolean hasPostalAddress(Resources resources, @NonNull Contact contact) {
+        boolean showPostalAddress = resources.getBoolean(
+                R.bool.config_show_postal_address);
+        return showPostalAddress && (!contact.getPostalAddresses().isEmpty());
+    }
+
+    /**
+     * Returns true if the contact has either phone number or postal address to show.
+     */
+    public static boolean hasContactDetail(Resources resources, @Nullable Contact contact) {
+        boolean hasContactDetail = contact != null
+                && (!contact.getNumbers().isEmpty() || DialerUtils.hasPostalAddress(
+                resources, contact));
+        return hasContactDetail;
+    }
 }
diff --git a/src/com/android/car/dialer/ui/contact/ContactListViewHolder.java b/src/com/android/car/dialer/ui/contact/ContactListViewHolder.java
index 8a35f87..9619151 100644
--- a/src/com/android/car/dialer/ui/contact/ContactListViewHolder.java
+++ b/src/com/android/car/dialer/ui/contact/ContactListViewHolder.java
@@ -126,8 +126,7 @@
             return;
         }
 
-        boolean hasContactDetail = contact != null
-                && (!contact.getNumbers().isEmpty() || hasPostalAddress(contact));
+        boolean hasContactDetail = DialerUtils.hasContactDetail(itemView.getResources(), contact);
 
         ViewUtils.setEnabled(mShowContactDetailView, hasContactDetail);
         ViewUtils.setVisible(mShowContactDetailView, hasContactDetail || forceShowButton);
@@ -140,12 +139,6 @@
         }
     }
 
-    private boolean hasPostalAddress(@NonNull Contact contact) {
-        boolean showPostalAddress = itemView.getResources().getBoolean(
-                R.bool.config_show_postal_address);
-        return showPostalAddress && (!contact.getPostalAddresses().isEmpty());
-    }
-
     /**
      * Recycles views.
      */
diff --git a/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java b/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java
index 574cee0..6ddbad7 100644
--- a/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java
+++ b/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java
@@ -18,6 +18,7 @@
 
 import android.app.AlertDialog;
 import android.os.Bundle;
+import android.widget.Toast;
 
 import androidx.lifecycle.ViewModelProviders;
 
@@ -89,7 +90,12 @@
             return;
         }
 
-        mDialogAdapter.setPhoneNumbers(contact, contact.getNumbers());
-        mCurrentDialog.show();
+        if (contact.getNumbers().isEmpty()) {
+            Toast.makeText(getContext(), R.string.no_phone_numbers, Toast.LENGTH_SHORT).show();
+            mCurrentDialog.dismiss();
+        } else {
+            mDialogAdapter.setPhoneNumbers(contact, contact.getNumbers());
+            mCurrentDialog.show();
+        }
     }
 }
diff --git a/src/com/android/car/dialer/ui/search/ContactResultViewHolder.java b/src/com/android/car/dialer/ui/search/ContactResultViewHolder.java
index ad1d139..3fd25b5 100644
--- a/src/com/android/car/dialer/ui/search/ContactResultViewHolder.java
+++ b/src/com/android/car/dialer/ui/search/ContactResultViewHolder.java
@@ -24,13 +24,16 @@
 import androidx.recyclerview.widget.RecyclerView;
 
 import com.android.car.dialer.R;
+import com.android.car.dialer.ui.common.DialerUtils;
 import com.android.car.dialer.ui.view.ContactAvatarOutputlineProvider;
 import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.TelecomUtils;
 
+import com.bumptech.glide.Glide;
+
 /**
- * A {@link androidx.recyclerview.widget.RecyclerView.ViewHolder} that will parse relevant
- * views out of a {@code contact_result} layout.
+ * A {@link androidx.recyclerview.widget.RecyclerView.ViewHolder} that will parse relevant views out
+ * of a {@code contact_result} layout.
  */
 public class ContactResultViewHolder extends RecyclerView.ViewHolder {
     private final Context mContext;
@@ -54,11 +57,21 @@
      * Populates the view that is represented by this ViewHolder with the information in the
      * provided {@link Contact}.
      */
-    public void bind(Contact contact) {
-        mContactCard.setOnClickListener(
-                v -> mOnShowContactDetailListener.onShowContactDetail(contact));
-
+    void bind(Contact contact) {
         mContactName.setText(contact.getDisplayName());
         TelecomUtils.setContactBitmapAsync(mContext, mContactPicture, contact);
+
+        if (DialerUtils.hasContactDetail(itemView.getResources(), contact)) {
+            mContactCard.setOnClickListener(
+                    v -> mOnShowContactDetailListener.onShowContactDetail(contact));
+        } else {
+            itemView.setEnabled(false);
+        }
+    }
+
+    void recycle() {
+        itemView.setEnabled(true);
+        mContactCard.setOnClickListener(null);
+        Glide.with(mContext).clear(mContactPicture);
     }
 }
diff --git a/src/com/android/car/dialer/ui/search/ContactResultsAdapter.java b/src/com/android/car/dialer/ui/search/ContactResultsAdapter.java
index 7b1ac1f..2a6542d 100644
--- a/src/com/android/car/dialer/ui/search/ContactResultsAdapter.java
+++ b/src/com/android/car/dialer/ui/search/ContactResultsAdapter.java
@@ -77,6 +77,11 @@
     }
 
     @Override
+    public void onViewRecycled(ContactResultViewHolder holder) {
+        holder.recycle();
+    }
+
+    @Override
     public int getItemViewType(int position) {
         // Only one type of view is created, so no need for an individualized view type.
         return 0;
diff --git a/src/com/android/car/dialer/ui/search/ContactResultsViewModel.java b/src/com/android/car/dialer/ui/search/ContactResultsViewModel.java
index 0607a30..eb15df8 100644
--- a/src/com/android/car/dialer/ui/search/ContactResultsViewModel.java
+++ b/src/com/android/car/dialer/ui/search/ContactResultsViewModel.java
@@ -171,8 +171,7 @@
         public QueryParam getQueryParam() {
             Uri lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_FILTER_URI,
                     Uri.encode(mSearchQueryLiveData.getValue()));
-            return new QueryParam(lookupUri, CONTACT_DETAILS_PROJECTION,
-                    ContactsContract.Contacts.HAS_PHONE_NUMBER + "!=0",
+            return new QueryParam(lookupUri, CONTACT_DETAILS_PROJECTION, null,
                     /* selectionArgs= */null, /* orderBy= */null);
         }
     }
diff --git a/tests/robotests/src/com/android/car/dialer/ui/search/ContactResultsFragmentTest.java b/tests/robotests/src/com/android/car/dialer/ui/search/ContactResultsFragmentTest.java
index 83c8e79..7c2910e 100644
--- a/tests/robotests/src/com/android/car/dialer/ui/search/ContactResultsFragmentTest.java
+++ b/tests/robotests/src/com/android/car/dialer/ui/search/ContactResultsFragmentTest.java
@@ -37,6 +37,7 @@
 import com.android.car.dialer.ui.contact.ContactDetailsViewModel;
 import com.android.car.telephony.common.Contact;
 import com.android.car.telephony.common.InMemoryPhoneBook;
+import com.android.car.telephony.common.PhoneNumber;
 import com.android.car.ui.recyclerview.CarUiRecyclerView;
 
 import org.junit.After;
@@ -50,6 +51,7 @@
 import org.robolectric.annotation.Config;
 
 import java.util.Arrays;
+import java.util.Collections;
 import java.util.List;
 
 @Config(shadows = {ShadowAndroidViewModelFactory.class})
@@ -71,6 +73,8 @@
     private Contact mMockContact;
     @Mock
     private Contact mContact1, mContact2, mContact3;
+    @Mock
+    private PhoneNumber mPhoneNumber;
 
     @Before
     public void setUp() {
@@ -84,8 +88,11 @@
                 ContactResultsViewModel.class, mMockContactResultsViewModel);
 
         when(mContact1.getDisplayName()).thenReturn(DISPLAY_NAMES[0]);
+        when(mContact1.getNumbers()).thenReturn(Collections.singletonList(mPhoneNumber));
         when(mContact2.getDisplayName()).thenReturn(DISPLAY_NAMES[1]);
+        when(mContact2.getNumbers()).thenReturn(Collections.singletonList(mPhoneNumber));
         when(mContact3.getDisplayName()).thenReturn(DISPLAY_NAMES[2]);
+        when(mContact3.getNumbers()).thenReturn(Collections.singletonList(mPhoneNumber));
     }
 
     @After