Implement the favorite picker page.

This lets you search for a contact and then select favorites to add
from a dialog with a list of phone numbers. Currently the numbers
already added as a favorite are not filtered out. And the actual
functionality to add a favorite isn't there yet.

Bug: 135949998
Test: Manually
Change-Id: Id20ab5e5d89f2e544cb73801ac52db7caa7c76e2
diff --git a/res/layout/phone_number_favorite_list_item.xml b/res/layout/phone_number_favorite_list_item.xml
new file mode 100644
index 0000000..94cb81e
--- /dev/null
+++ b/res/layout/phone_number_favorite_list_item.xml
@@ -0,0 +1,56 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2019 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.
+-->
+<androidx.constraintlayout.widget.ConstraintLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:paddingStart="?android:listPreferredItemPaddingStart"
+    android:paddingEnd="?android:listPreferredItemPaddingEnd"
+    android:paddingTop="@dimen/phone_number_favorite_list_item_vertical_padding"
+    android:paddingBottom="@dimen/phone_number_favorite_list_item_vertical_padding">
+
+    <TextView
+        android:id="@+id/phone_number"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_toEndOf="@id/phone_number_checkbox"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toTopOf="@id/phone_number_description"
+        app:layout_constraintStart_toStartOf="parent"
+        app:layout_constraintVertical_chainStyle="packed"/>
+    <TextView
+        android:id="@+id/phone_number_description"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/phone_number"
+        android:layout_toEndOf="@id/phone_number_checkbox"
+        android:textAppearance="?android:attr/textAppearanceSmall"
+        app:layout_constraintTop_toBottomOf="@id/phone_number"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintStart_toStartOf="parent"/>
+    <ImageView
+        android:id="@+id/phone_number_checkbox"
+        android:src="@drawable/ic_favorite_activatable"
+        android:layout_width="@dimen/primary_icon_size"
+        android:layout_height="@dimen/primary_icon_size"
+        android:tint="@color/primary_icon_color"
+        app:layout_constraintTop_toTopOf="parent"
+        app:layout_constraintBottom_toBottomOf="parent"
+        app:layout_constraintEnd_toEndOf="parent"/>
+
+</androidx.constraintlayout.widget.ConstraintLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 2a9f5c4..20a8ed4 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -139,6 +139,7 @@
     <dimen name="contact_avatar_corner_radius_percent" format="float">0.5</dimen>
     <dimen name="touch_target_width">156dp</dimen>
     <dimen name="phone_number_radio_list_padding">@*android:dimen/car_padding_2</dimen>
+    <dimen name="phone_number_favorite_list_item_vertical_padding">@*android:dimen/car_padding_2</dimen>
 
     <!-- Toolbar -->
     <!-- Half of the difference between car_margin and touch_target_size-->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index bc7996a..4b48392 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -69,6 +69,10 @@
     <string name="add_favorite_button">Add a favorite</string>
     <!-- Error message shown when on the favorites page without any favorites added [CHAR_LIMIT=80] -->
     <string name="favorites_empty">You haven\'t added any favorites yet</string>
+    <!-- Button to cancel the dialog where you pick a phone number to add as a favorite [CHAR_LIMIT=30] -->
+    <string name="cancel_add_favorites_dialog">@android:string/cancel</string>
+    <!-- Button to confirm the dialog where you pick a phone number to add as a favorite [CHAR_LIMIT=30] -->
+    <string name="confirm_add_favorites_dialog">@android:string/ok</string>
 
     <!-- Keypad strings-->
     <string name="one" translatable="false">1</string>
diff --git a/src/com/android/car/dialer/ui/common/FavoritePhoneNumberListAdapter.java b/src/com/android/car/dialer/ui/common/FavoritePhoneNumberListAdapter.java
new file mode 100644
index 0000000..aeb609b
--- /dev/null
+++ b/src/com/android/car/dialer/ui/common/FavoritePhoneNumberListAdapter.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2019 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.car.dialer.ui.common;
+
+import android.content.Context;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+
+import com.android.car.dialer.R;
+import com.android.car.telephony.common.PhoneNumber;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * {@link ArrayAdapter} that presents the {@link PhoneNumber} and its type as two line list
+ * item with stars to indicate selection.
+ */
+public class FavoritePhoneNumberListAdapter extends ArrayAdapter<PhoneNumber> {
+    private final Context mContext;
+
+    public FavoritePhoneNumberListAdapter(Context context) {
+        super(context, R.layout.phone_number_favorite_list_item,
+                R.id.phone_number, new ArrayList<>());
+        mContext = context;
+    }
+
+    /** Sets the phone numbers to display */
+    public void setPhoneNumbers(List<PhoneNumber> phoneNumbers) {
+        clear();
+        addAll(phoneNumbers);
+    }
+
+    @Override
+    public View getView(int position, @Nullable View convertView,
+            @NonNull ViewGroup parent) {
+        View view = super.getView(position, convertView, parent);
+        PhoneNumber phoneNumber = getItem(position);
+        if (phoneNumber == null) {
+            return view;
+        }
+        TextView phoneNumberView = view.findViewById(R.id.phone_number);
+        phoneNumberView.setText(phoneNumber.getRawNumber());
+        TextView phoneNumberDescriptionView = view.findViewById(R.id.phone_number_description);
+        CharSequence readableLabel = phoneNumber.getReadableLabel(mContext.getResources());
+        if (phoneNumber.isPrimary()) {
+            phoneNumberDescriptionView.setText(
+                    mContext.getString(R.string.primary_number_description, readableLabel));
+        } else {
+            phoneNumberDescriptionView.setText(readableLabel);
+        }
+        return view;
+    }
+}
diff --git a/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java b/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java
new file mode 100644
index 0000000..5c965bb
--- /dev/null
+++ b/src/com/android/car/dialer/ui/favorite/AddFavoriteFragment.java
@@ -0,0 +1,99 @@
+/*
+ * Copyright (C) 2019 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.car.dialer.ui.favorite;
+
+import android.app.AlertDialog;
+import android.net.Uri;
+import android.os.Bundle;
+import android.widget.ListView;
+import android.widget.Toast;
+
+import com.android.car.dialer.R;
+import com.android.car.dialer.livedata.ContactDetailsLiveData;
+import com.android.car.dialer.ui.common.FavoritePhoneNumberListAdapter;
+import com.android.car.dialer.ui.search.ContactResultsFragment;
+import com.android.car.telephony.common.PhoneNumber;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/** A fragment that allows the user to search for and select favorite phone numbers */
+public class AddFavoriteFragment extends ContactResultsFragment {
+
+    /** Creates a new instance of AddFavoriteFragment */
+    public static AddFavoriteFragment newInstance() {
+        return new AddFavoriteFragment();
+    }
+
+    private ContactDetailsLiveData mContactDetails;
+    private AlertDialog mCurrentDialog;
+    private FavoritePhoneNumberListAdapter mDialogAdapter;
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mDialogAdapter = new FavoritePhoneNumberListAdapter(getContext());
+
+        Set<PhoneNumber> selectedNumbers = new HashSet<>();
+        mCurrentDialog = new AlertDialog.Builder(getContext())
+                .setTitle(R.string.select_number_dialog_title)
+                .setAdapter(mDialogAdapter,
+                        null)
+                .setNegativeButton(R.string.cancel_add_favorites_dialog,
+                        null)
+                .setPositiveButton(R.string.confirm_add_favorites_dialog,
+                        (d, which) -> {
+                            String result = "Not yet implemented, but you picked ";
+                            for (PhoneNumber number : selectedNumbers) {
+                                result += number.getNumber() + ", ";
+                            }
+                            Toast.makeText(getContext(), result, Toast.LENGTH_LONG).show();
+                            getFragmentManager().popBackStackImmediate();
+                        })
+                .create();
+
+        mCurrentDialog.getListView().setItemsCanFocus(false);
+        mCurrentDialog.getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
+        mCurrentDialog.getListView().setOnItemClickListener((parent, view2, position, id) -> {
+            PhoneNumber number = mDialogAdapter.getItem(position);
+            if (view2.isActivated()) {
+                selectedNumbers.add(number);
+            } else {
+                selectedNumbers.remove(number);
+            }
+        });
+    }
+
+    @Override
+    public void onShowContactDetail(Uri contactLookupUri) {
+        if (mContactDetails != null) {
+            mContactDetails.removeObservers(this);
+        }
+
+        mContactDetails = new ContactDetailsLiveData(getContext(), contactLookupUri);
+        mContactDetails.observe(this, (contact) -> {
+            if (contact == null) {
+                mCurrentDialog.dismiss();
+                return;
+            }
+
+            mDialogAdapter.setPhoneNumbers(contact.getNumbers());
+            mCurrentDialog.show();
+        });
+    }
+}
diff --git a/src/com/android/car/dialer/ui/favorite/FavoriteAdapter.java b/src/com/android/car/dialer/ui/favorite/FavoriteAdapter.java
index ae05121..16ffd2d 100644
--- a/src/com/android/car/dialer/ui/favorite/FavoriteAdapter.java
+++ b/src/com/android/car/dialer/ui/favorite/FavoriteAdapter.java
@@ -20,7 +20,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Toast;
 
 import androidx.recyclerview.widget.RecyclerView;
 
@@ -38,8 +37,15 @@
 public class FavoriteAdapter extends RecyclerView.Adapter<FavoriteContactViewHolder> {
     private static final String TAG = "CD.FavoriteAdapter";
 
+    /** Listener interface for when the add favorite button is clicked */
+    public interface OnAddFavoriteClickedListener {
+        /** Called when the add favorite button is clicked */
+        void onAddFavoriteClicked();
+    }
+
     private List<Contact> mFavoriteContacts = Collections.emptyList();
     private OnItemClickedListener<Contact> mListener;
+    private OnAddFavoriteClickedListener mAddFavoriteListener;
 
     /** Sets the favorite contact list. */
     public void setFavoriteContacts(List<Contact> favoriteContacts) {
@@ -67,18 +73,19 @@
 
         if (position >= mFavoriteContacts.size()) {
             viewHolder.onBindAddFavorite(context);
-            viewHolder.itemView.setOnClickListener((v) ->
-                    Toast.makeText(context, "Not yet implemented", Toast.LENGTH_LONG).show());
+            viewHolder.itemView.setOnClickListener((v) -> {
+                if (mAddFavoriteListener != null) {
+                    mAddFavoriteListener.onAddFavoriteClicked();
+                }
+            });
         } else {
             Contact contact = mFavoriteContacts.get(position);
             viewHolder.onBind(context, contact);
-            viewHolder.itemView.setOnClickListener((v) -> onItemViewClicked(contact));
-        }
-    }
-
-    private void onItemViewClicked(Contact contact) {
-        if (mListener != null) {
-            mListener.onItemClicked(contact);
+            viewHolder.itemView.setOnClickListener((v) -> {
+                if (mListener != null) {
+                    mListener.onItemClicked(contact);
+                }
+            });
         }
     }
 
@@ -88,4 +95,12 @@
     public void setOnListItemClickedListener(OnItemClickedListener<Contact> listener) {
         mListener = listener;
     }
+
+    /**
+     * Sets a {@link OnAddFavoriteClickedListener listener} which will be called when the
+     * "Add favorite" button is clicked.
+     */
+    public void setOnAddFavoriteClickedListener(OnAddFavoriteClickedListener listener) {
+        mAddFavoriteListener = listener;
+    }
 }
diff --git a/src/com/android/car/dialer/ui/favorite/FavoriteFragment.java b/src/com/android/car/dialer/ui/favorite/FavoriteFragment.java
index 7e3573d..61b3ff4 100644
--- a/src/com/android/car/dialer/ui/favorite/FavoriteFragment.java
+++ b/src/com/android/car/dialer/ui/favorite/FavoriteFragment.java
@@ -20,7 +20,6 @@
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Toast;
 
 import androidx.fragment.app.Fragment;
 import androidx.lifecycle.ViewModelProviders;
@@ -60,7 +59,7 @@
         });
 
         emptyPage.findViewById(R.id.add_favorite_button).setOnClickListener(v ->
-                Toast.makeText(getContext(), "Not yet implemented", Toast.LENGTH_LONG).show());
+                pushContentFragment(AddFavoriteFragment.newInstance(), null));
 
         return view;
     }
diff --git a/src/com/android/car/dialer/ui/favorite/FavoriteListFragment.java b/src/com/android/car/dialer/ui/favorite/FavoriteListFragment.java
index 4158372..4379fa7 100644
--- a/src/com/android/car/dialer/ui/favorite/FavoriteListFragment.java
+++ b/src/com/android/car/dialer/ui/favorite/FavoriteListFragment.java
@@ -50,6 +50,7 @@
         getRecyclerView().setItemAnimator(null);
 
         FavoriteAdapter adapter = new FavoriteAdapter();
+        adapter.setOnAddFavoriteClickedListener(this::onAddFavoriteClicked);
 
         FavoriteViewModel favoriteViewModel = ViewModelProviders.of(getActivity()).get(
                 FavoriteViewModel.class);
@@ -73,6 +74,10 @@
                 UiCallManager.get().placeCall(phoneNumber.getRawNumber()));
     }
 
+    private void onAddFavoriteClicked() {
+        pushContentFragment(AddFavoriteFragment.newInstance(), null);
+    }
+
     private class ItemSpacingDecoration extends RecyclerView.ItemDecoration {
 
         @Override