Merge "Implemented disambig dialog for new speed dial fragment."
diff --git a/java/com/android/dialer/speeddial/DisambigDialog.java b/java/com/android/dialer/speeddial/DisambigDialog.java
new file mode 100644
index 0000000..ca02f41
--- /dev/null
+++ b/java/com/android/dialer/speeddial/DisambigDialog.java
@@ -0,0 +1,244 @@
+/*
+ * 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.speeddial;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
+import android.content.ContentResolver;
+import android.content.res.Resources;
+import android.database.Cursor;
+import android.os.Bundle;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.support.annotation.Nullable;
+import android.support.annotation.VisibleForTesting;
+import android.text.TextUtils;
+import android.util.ArraySet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.dialer.callintent.CallInitiationType;
+import com.android.dialer.callintent.CallIntentBuilder;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.common.concurrent.DialerExecutor.Worker;
+import com.android.dialer.common.concurrent.DialerExecutorComponent;
+import com.android.dialer.duo.DuoComponent;
+import com.android.dialer.precall.PreCall;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Set;
+
+/** Disambiguation dialog for favorite contacts in {@link SpeedDialFragment}. */
+public class DisambigDialog extends DialogFragment {
+
+  @VisibleForTesting public static final String DISAMBIG_DIALOG_TAG = "disambig_dialog";
+  private static final String DISAMBIG_DIALOG_WORKER_TAG = "disambig_dialog_worker";
+
+  private final Set<String> phoneNumbers = new ArraySet<>();
+  private LinearLayout container;
+  private String lookupKey;
+
+  /** Show a disambiguation dialog for a starred contact without a favorite communication avenue. */
+  public static DisambigDialog show(String lookupKey, FragmentManager manager) {
+    DisambigDialog dialog = new DisambigDialog();
+    dialog.lookupKey = lookupKey;
+    dialog.show(manager, DISAMBIG_DIALOG_TAG);
+    return dialog;
+  }
+
+  @Override
+  public Dialog onCreateDialog(Bundle savedInstanceState) {
+    LayoutInflater inflater = getActivity().getLayoutInflater();
+    View view = inflater.inflate(R.layout.disambig_dialog_layout, null, false);
+    container = view.findViewById(R.id.communication_avenue_container);
+    return new AlertDialog.Builder(getActivity()).setView(view).create();
+  }
+
+  @Override
+  public void onResume() {
+    super.onResume();
+    lookupContactInfo();
+  }
+
+  @Override
+  public void onPause() {
+    super.onPause();
+    // TODO(calderwoodra): for simplicity, just dismiss the dialog on configuration change and
+    // consider changing this later.
+    dismiss();
+  }
+
+  private void lookupContactInfo() {
+    DialerExecutorComponent.get(getContext())
+        .dialerExecutorFactory()
+        .createUiTaskBuilder(
+            getFragmentManager(),
+            DISAMBIG_DIALOG_WORKER_TAG,
+            new LookupContactInfoWorker(getContext().getContentResolver()))
+        .onSuccess(this::insertOptions)
+        .onFailure(this::onLookupFailed)
+        .build()
+        .executeParallel(lookupKey);
+  }
+
+  /**
+   * Inflates and inserts the following in the dialog:
+   *
+   * <ul>
+   *   <li>Header for each unique phone number
+   *   <li>Clickable video option if the phone number is video reachable (ViLTE, Duo)
+   *   <li>Clickable voice option
+   * </ul>
+   */
+  private void insertOptions(Cursor cursor) {
+    if (!cursorIsValid(cursor)) {
+      dismiss();
+      return;
+    }
+
+    do {
+      String number = cursor.getString(LookupContactInfoWorker.NUMBER_INDEX);
+      // TODO(calderwoodra): improve this to include fuzzy matching
+      if (phoneNumbers.add(number)) {
+        insertOption(
+            number,
+            getLabel(getContext().getResources(), cursor),
+            isVideoReachable(cursor, number));
+      }
+    } while (cursor.moveToNext());
+    cursor.close();
+    // TODO(calderwoodra): set max height of the scrollview. Might need to override onMeasure.
+  }
+
+  /** Returns true if the given number is ViLTE reachable or Duo reachable. */
+  private boolean isVideoReachable(Cursor cursor, String number) {
+    boolean isVideoReachable = cursor.getInt(LookupContactInfoWorker.PHONE_PRESENCE_INDEX) == 1;
+    if (!isVideoReachable) {
+      isVideoReachable = DuoComponent.get(getContext()).getDuo().isReachable(getContext(), number);
+    }
+    return isVideoReachable;
+  }
+
+  /** Inserts a group of options for a specific phone number. */
+  private void insertOption(String number, String phoneType, boolean isVideoReachable) {
+    View view =
+        getActivity()
+            .getLayoutInflater()
+            .inflate(R.layout.disambig_option_layout, container, false);
+    ((TextView) view.findViewById(R.id.phone_type)).setText(phoneType);
+    ((TextView) view.findViewById(R.id.phone_number)).setText(number);
+
+    if (isVideoReachable) {
+      View videoOption = view.findViewById(R.id.video_call_container);
+      videoOption.setOnClickListener(v -> onVideoOptionClicked(number));
+      videoOption.setVisibility(View.VISIBLE);
+    }
+    View voiceOption = view.findViewById(R.id.voice_call_container);
+    voiceOption.setOnClickListener(v -> onVoiceOptionClicked(number));
+    container.addView(view);
+  }
+
+  private void onVideoOptionClicked(String number) {
+    // TODO(calderwoodra): save this option if remember is checked
+    // TODO(calderwoodra): place a duo call if possible
+    PreCall.start(
+        getContext(),
+        new CallIntentBuilder(number, CallInitiationType.Type.SPEED_DIAL).setIsVideoCall(true));
+  }
+
+  private void onVoiceOptionClicked(String number) {
+    // TODO(calderwoodra): save this option if remember is checked
+    PreCall.start(getContext(), new CallIntentBuilder(number, CallInitiationType.Type.SPEED_DIAL));
+  }
+
+  // TODO(calderwoodra): handle CNAP and cequint types.
+  // TODO(calderwoodra): unify this into a utility method with CallLogAdapter#getNumberType
+  private static String getLabel(Resources resources, Cursor cursor) {
+    int numberType = cursor.getInt(LookupContactInfoWorker.PHONE_TYPE_INDEX);
+    String numberLabel = cursor.getString(LookupContactInfoWorker.PHONE_LABEL_INDEX);
+
+    // Returns empty label instead of "custom" if the custom label is empty.
+    if (numberType == Phone.TYPE_CUSTOM && TextUtils.isEmpty(numberLabel)) {
+      return "";
+    }
+    return (String) Phone.getTypeLabel(resources, numberType, numberLabel);
+  }
+
+  // Checks if the cursor is valid and logs an error if there are any issues.
+  private static boolean cursorIsValid(Cursor cursor) {
+    if (cursor == null) {
+      LogUtil.e("DisambigDialog.insertOptions", "cursor null.");
+      return false;
+    } else if (cursor.isClosed()) {
+      LogUtil.e("DisambigDialog.insertOptions", "cursor closed.");
+      cursor.close();
+      return false;
+    } else if (!cursor.moveToFirst()) {
+      LogUtil.e("DisambigDialog.insertOptions", "cursor empty.");
+      cursor.close();
+      return false;
+    }
+    return true;
+  }
+
+  private void onLookupFailed(Throwable throwable) {
+    LogUtil.e("DisambigDialog.onLookupFailed", null, throwable);
+    insertOptions(null);
+  }
+
+  private static class LookupContactInfoWorker implements Worker<String, Cursor> {
+
+    static final int NUMBER_INDEX = 0;
+    static final int PHONE_TYPE_INDEX = 1;
+    static final int PHONE_LABEL_INDEX = 2;
+    static final int PHONE_PRESENCE_INDEX = 3;
+
+    private static final String[] projection =
+        new String[] {Phone.NUMBER, Phone.TYPE, Phone.LABEL, Phone.CARRIER_PRESENCE};
+    private final ContentResolver resolver;
+
+    LookupContactInfoWorker(ContentResolver resolver) {
+      this.resolver = resolver;
+    }
+
+    @Nullable
+    @Override
+    public Cursor doInBackground(@Nullable String lookupKey) throws Throwable {
+      if (TextUtils.isEmpty(lookupKey)) {
+        LogUtil.e("LookupConctactInfoWorker.doInBackground", "contact id unsest.");
+        return null;
+      }
+      return resolver.query(
+          Phone.CONTENT_URI, projection, Phone.LOOKUP_KEY + " = ?", new String[] {lookupKey}, null);
+    }
+  }
+
+  @VisibleForTesting
+  public static String[] getProjectionForTesting() {
+    ArrayList<String> projection =
+        new ArrayList<>(Arrays.asList(LookupContactInfoWorker.projection));
+    projection.add(Phone.LOOKUP_KEY);
+    return projection.toArray(new String[projection.size()]);
+  }
+
+  @VisibleForTesting
+  public LinearLayout getContainer() {
+    return container;
+  }
+}
diff --git a/java/com/android/dialer/speeddial/FavoritesViewHolder.java b/java/com/android/dialer/speeddial/FavoritesViewHolder.java
index 0cde716..c25b05e 100644
--- a/java/com/android/dialer/speeddial/FavoritesViewHolder.java
+++ b/java/com/android/dialer/speeddial/FavoritesViewHolder.java
@@ -45,8 +45,10 @@
   private final TextView phoneType;
   private final FrameLayout videoCallIcon;
 
+  private boolean hasDefaultNumber;
   private boolean isVideoCall;
   private String number;
+  private String lookupKey;
 
   public FavoritesViewHolder(View view, FavoriteContactsListener listener) {
     super(view);
@@ -67,7 +69,7 @@
 
     String name = cursor.getString(StrequentContactsCursorLoader.PHONE_DISPLAY_NAME);
     long contactId = cursor.getLong(StrequentContactsCursorLoader.PHONE_ID);
-    String lookupKey = cursor.getString(StrequentContactsCursorLoader.PHONE_LOOKUP_KEY);
+    lookupKey = cursor.getString(StrequentContactsCursorLoader.PHONE_LOOKUP_KEY);
     Uri contactUri = Contacts.getLookupUri(contactId, lookupKey);
 
     String photoUri = cursor.getString(StrequentContactsCursorLoader.PHONE_PHOTO_URI);
@@ -82,6 +84,9 @@
     nameView.setText(name);
     phoneType.setText(getLabel(context.getResources(), cursor));
     videoCallIcon.setVisibility(isVideoCall ? View.VISIBLE : View.GONE);
+
+    // TODO(calderwoodra): Update this to include communication avenues also
+    hasDefaultNumber = cursor.getInt(StrequentContactsCursorLoader.PHONE_IS_SUPER_PRIMARY) != 0;
   }
 
   // TODO(calderwoodra): handle CNAP and cequint types.
@@ -99,7 +104,11 @@
 
   @Override
   public void onClick(View v) {
-    listener.onClick(number, isVideoCall);
+    if (hasDefaultNumber) {
+      listener.onClick(number, isVideoCall);
+    } else {
+      listener.onAmbiguousContactClicked(lookupKey);
+    }
   }
 
   @Override
@@ -112,6 +121,9 @@
   /** Listener/callback for {@link FavoritesViewHolder} actions. */
   public interface FavoriteContactsListener {
 
+    /** Called when the user clicks on a favorite contact that doesn't have a default number. */
+    void onAmbiguousContactClicked(String contactId);
+
     /** Called when the user clicks on a favorite contact. */
     void onClick(String number, boolean isVideoCall);
 
diff --git a/java/com/android/dialer/speeddial/SpeedDialFragment.java b/java/com/android/dialer/speeddial/SpeedDialFragment.java
index 08861da..979c894 100644
--- a/java/com/android/dialer/speeddial/SpeedDialFragment.java
+++ b/java/com/android/dialer/speeddial/SpeedDialFragment.java
@@ -98,6 +98,11 @@
   private class SpeedDialFavoritesListener implements FavoriteContactsListener {
 
     @Override
+    public void onAmbiguousContactClicked(String lookupKey) {
+      DisambigDialog.show(lookupKey, getFragmentManager());
+    }
+
+    @Override
     public void onClick(String number, boolean isVideoCall) {
       // TODO(calderwoodra): add logic for duo video calls
       PreCall.start(
diff --git a/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java b/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java
index f5f0045..e9e3e32 100644
--- a/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java
+++ b/java/com/android/dialer/speeddial/StrequentContactsCursorLoader.java
@@ -18,7 +18,6 @@
 
 import android.content.Context;
 import android.content.CursorLoader;
-import android.database.ContentObserver;
 import android.database.Cursor;
 import android.database.MatrixCursor;
 import android.net.Uri;
@@ -58,8 +57,6 @@
         Phone.CONTACT_ID, // 11
       };
 
-  private final ContentObserver contentObserver = new ForceLoadContentObserver();
-
   StrequentContactsCursorLoader(Context context) {
     super(
         context,
@@ -97,8 +94,4 @@
   public Cursor loadInBackground() {
     return SpeedDialCursor.newInstance(super.loadInBackground());
   }
-
-  ContentObserver getContentObserver() {
-    return contentObserver;
-  }
 }
diff --git a/java/com/android/dialer/speeddial/res/layout/disambig_dialog_layout.xml b/java/com/android/dialer/speeddial/res/layout/disambig_dialog_layout.xml
new file mode 100644
index 0000000..3562058
--- /dev/null
+++ b/java/com/android/dialer/speeddial/res/layout/disambig_dialog_layout.xml
@@ -0,0 +1,66 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content">
+
+  <TextView
+      android:id="@+id/disambig_dialog_title"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:gravity="center_vertical"
+      android:minHeight="64dp"
+      android:paddingStart="24dp"
+      android:paddingEnd="24dp"
+      android:elevation="1dp"
+      android:text="@string/speed_dial_disambig_dialog_title"
+      android:textSize="20sp"
+      android:textColor="@color/dialer_primary_text_color"
+      android:fontFamily="sans-serif-medium"
+      android:background="@android:color/white"/>
+
+  <ScrollView
+      android:id="@+id/disambig_scrollview"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content">
+
+    <LinearLayout
+        android:id="@+id/communication_avenue_container"
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"/>
+  </ScrollView>
+
+  <FrameLayout
+      android:id="@+id/remember_this_choice_container"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:layout_gravity="bottom"
+      android:minHeight="56dp"
+      android:padding="12dp"
+      android:elevation="4dp"
+      android:background="@android:color/white">
+
+    <CheckBox
+        android:id="@+id/remember_this_choice_checkbox"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:text="@string/speed_dial_remember_this_choice"/>
+  </FrameLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/java/com/android/dialer/speeddial/res/layout/disambig_option_layout.xml b/java/com/android/dialer/speeddial/res/layout/disambig_option_layout.xml
new file mode 100644
index 0000000..097ac40
--- /dev/null
+++ b/java/com/android/dialer/speeddial/res/layout/disambig_option_layout.xml
@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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
+ -->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/disambig_option_container"
+    android:orientation="vertical"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:minHeight="56dp"
+    android:layout_marginBottom="8dp">
+
+  <LinearLayout
+      android:orientation="vertical"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:minHeight="56dp"
+      android:gravity="center_vertical"
+      android:paddingStart="24dp"
+      android:paddingEnd="24dp">
+
+    <TextView
+        android:id="@+id/phone_type"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@style/PrimaryText"/>
+
+    <TextView
+        android:id="@+id/phone_number"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        style="@style/SecondaryText"/>
+  </LinearLayout>
+
+  <LinearLayout
+      android:id="@+id/video_call_container"
+      android:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:paddingStart="24dp"
+      android:paddingEnd="24dp"
+      android:minHeight="56dp"
+      android:background="?android:attr/selectableItemBackground"
+      android:visibility="gone"
+      android:contentDescription="@string/disambig_option_video_call">
+
+    <ImageView
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_gravity="center_vertical"
+        android:tint="@color/dialer_secondary_text_color"
+        android:src="@drawable/quantum_ic_videocam_vd_theme_24"/>
+
+    <TextView
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_gravity="center_vertical"
+        android:text="@string/disambig_option_video_call"
+        style="@style/PrimaryText"/>
+  </LinearLayout>
+
+  <LinearLayout
+      android:id="@+id/voice_call_container"
+      android:orientation="horizontal"
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:paddingStart="24dp"
+      android:paddingEnd="24dp"
+      android:minHeight="56dp"
+      android:background="?android:attr/selectableItemBackground"
+      android:contentDescription="@string/disambig_option_voice_call">
+
+    <ImageView
+        android:id="@+id/disambig_option_icon"
+        android:layout_width="24dp"
+        android:layout_height="24dp"
+        android:layout_gravity="center_vertical"
+        android:tint="@color/dialer_secondary_text_color"
+        android:src="@drawable/quantum_ic_phone_vd_theme_24"/>
+
+    <TextView
+        android:id="@+id/disambig_option_text"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_marginStart="12dp"
+        android:layout_gravity="center_vertical"
+        android:text="@string/disambig_option_voice_call"
+        style="@style/PrimaryText"/>
+  </LinearLayout>
+</LinearLayout>
\ No newline at end of file
diff --git a/java/com/android/dialer/speeddial/res/values/dimens.xml b/java/com/android/dialer/speeddial/res/values/dimens.xml
index 5929df8..74b509b 100644
--- a/java/com/android/dialer/speeddial/res/values/dimens.xml
+++ b/java/com/android/dialer/speeddial/res/values/dimens.xml
@@ -15,4 +15,5 @@
   ~ limitations under the License
   -->
 <resources>
+  <dimen name="scrollview_max_height">280dp</dimen>
 </resources>
\ No newline at end of file
diff --git a/java/com/android/dialer/speeddial/res/values/strings.xml b/java/com/android/dialer/speeddial/res/values/strings.xml
index d64d035..677f772 100644
--- a/java/com/android/dialer/speeddial/res/values/strings.xml
+++ b/java/com/android/dialer/speeddial/res/values/strings.xml
@@ -24,6 +24,20 @@
   <!-- text for a button that prompts the user to add a contact to their favorites. [CHAR LIMIT=12] -->
   <string name="speed_dial_add_button_text">Add</string>
 
+  <!-- text for a checkbox in a dialog that prompts the user to select a phone number from a list.
+    If the user checks this box, we will remember their selection and never ask for it again. [CHAR LIMIT=NONE]-->
+  <string name="speed_dial_remember_this_choice">Remember this choice</string>
+
+  <!-- Title of a dialog asking the user to choose their favorite mode of communication for a
+    specific contact where communication modes are video calling and voice calling. [CHAR LIMIT=NONE]-->
+  <string name="speed_dial_disambig_dialog_title">Choose a Favorite mode</string>
+
+  <!-- Text for a button that places a video call [CHAR LIMIT=15]-->
+  <string name="disambig_option_video_call">Video call</string>
+
+  <!-- Text for a button that places a phone/voice call [CHAR LIMIT=15]-->
+  <string name="disambig_option_voice_call">Call</string>
+
   <!-- Title for screen prompting the user to select a contact to mark as a favorite. [CHAR LIMIT=NONE] -->
   <string name="add_favorite_activity_title">Add Favorite</string>
 </resources>
\ No newline at end of file