Show dialog if multiple candidates exist

In GB we show a list of phone number to call in Favorites
screen, while we don't now.

Also refrain PhoneNumberInteraction from showing a dialog
by itself but let a nested class do instead. showDialog() is now
obsolete and we should use DialogFragment instead.

Bug: 4743008
Change-Id: I202963c2f03424f07ee386bd9713fde4091a0ae2
diff --git a/src/com/android/contacts/CallContactActivity.java b/src/com/android/contacts/CallContactActivity.java
index 77bf20c..b7c472a 100644
--- a/src/com/android/contacts/CallContactActivity.java
+++ b/src/com/android/contacts/CallContactActivity.java
@@ -32,12 +32,9 @@
  */
 public class CallContactActivity extends ContactsActivity implements OnDismissListener {
 
-    private PhoneNumberInteraction mPhoneNumberInteraction;
-
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-        mPhoneNumberInteraction = new PhoneNumberInteraction(this, false, this);
 
         Uri contactUri = getIntent().getData();
         if (contactUri == null) {
@@ -51,7 +48,7 @@
         }
 
         if (Contacts.CONTENT_ITEM_TYPE.equals(getContentResolver().getType(contactUri))) {
-            mPhoneNumberInteraction.startInteraction(contactUri);
+            PhoneNumberInteraction.startInteractionForPhoneCall(this, contactUri);
         } else {
             startActivity(new Intent(Intent.ACTION_CALL_PRIVILEGED, contactUri));
             finish();
@@ -64,14 +61,4 @@
             finish();
         }
     }
-
-    @Override
-    protected Dialog onCreateDialog(int id, Bundle args) {
-        return mPhoneNumberInteraction.onCreateDialog(id, args);
-    }
-
-    @Override
-    protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
-        mPhoneNumberInteraction.onPrepareDialog(id, dialog, args);
-    }
 }
diff --git a/src/com/android/contacts/Collapser.java b/src/com/android/contacts/Collapser.java
index 5b5d5a0..3b2f2a9 100644
--- a/src/com/android/contacts/Collapser.java
+++ b/src/com/android/contacts/Collapser.java
@@ -43,7 +43,7 @@
 
     /**
      * Collapses a list of Collapsible items into a list of collapsed items. Items are collapsed
-     * if {@link Collapsible#shouldCollapseWith(Object)} returns strue, and are collapsed
+     * if {@link Collapsible#shouldCollapseWith(Object)} returns true, and are collapsed
      * through the {@Link Collapsible#collapseWith(Object)} function implemented by the data item.
      *
      * @param list List of Objects of type <T extends Collapsible<T>> to be collapsed.
diff --git a/src/com/android/contacts/activities/DialtactsActivity.java b/src/com/android/contacts/activities/DialtactsActivity.java
index 931b600..ed80eb1 100644
--- a/src/com/android/contacts/activities/DialtactsActivity.java
+++ b/src/com/android/contacts/activities/DialtactsActivity.java
@@ -94,9 +94,6 @@
      */
     private int mLastManuallySelectedTab;
 
-    // TODO: It would be great to eventually remove all interactions and replace by DialogFragments
-    private PhoneNumberInteraction mPhoneNumberCallInteraction;
-
     @Override
     protected void onCreate(Bundle icicle) {
         super.onCreate(icicle);
@@ -395,13 +392,6 @@
         }
     }
 
-    private PhoneNumberInteraction getPhoneNumberCallInteraction() {
-        if (mPhoneNumberCallInteraction == null) {
-            mPhoneNumberCallInteraction = new PhoneNumberInteraction(this, false, null);
-        }
-        return mPhoneNumberCallInteraction;
-    }
-
     @Override
     protected void onPostCreate(Bundle savedInstanceState) {
         super.onPostCreate(savedInstanceState);
@@ -482,7 +472,8 @@
 
         @Override
         public void onCallContactAction(Uri contactUri) {
-            getPhoneNumberCallInteraction().startInteraction(contactUri);
+            PhoneNumberInteraction.startInteractionForPhoneCall(
+                    DialtactsActivity.this, contactUri);
         }
 
         @Override
@@ -494,7 +485,8 @@
             new StrequentContactListFragment.Listener() {
         @Override
         public void onContactSelected(Uri contactUri) {
-            getPhoneNumberCallInteraction().startInteraction(contactUri);
+            PhoneNumberInteraction.startInteractionForPhoneCall(
+                    DialtactsActivity.this, contactUri);
         }
     };
 
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index c247d43..55acdad 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -119,9 +119,6 @@
 
     private GroupDetailFragment mGroupDetailFragment;
 
-    private PhoneNumberInteraction mPhoneNumberCallInteraction;
-    private PhoneNumberInteraction mSendTextMessageInteraction;
-
     private boolean mSearchInitiated;
 
     private ContactListFilterController mContactListFilterController;
@@ -637,12 +634,12 @@
 
         @Override
         public void onCallContactAction(Uri contactUri) {
-            getPhoneNumberCallInteraction().startInteraction(contactUri);
+            PhoneNumberInteraction.startInteractionForPhoneCall(PeopleActivity.this, contactUri);
         }
 
         @Override
         public void onSmsContactAction(Uri contactUri) {
-            getSendTextMessageInteraction().startInteraction(contactUri);
+            PhoneNumberInteraction.startInteractionForTextMessage(PeopleActivity.this, contactUri);
         }
 
         @Override
@@ -935,32 +932,6 @@
     }
 
     @Override
-    protected Dialog onCreateDialog(int id, Bundle bundle) {
-        if (DialogManager.isManagedId(id)) return mDialogManager.onCreateDialog(id, bundle);
-
-        Dialog dialog = getPhoneNumberCallInteraction().onCreateDialog(id, bundle);
-        if (dialog != null) return dialog;
-
-        dialog = getSendTextMessageInteraction().onCreateDialog(id, bundle);
-        if (dialog != null) return dialog;
-
-        return super.onCreateDialog(id, bundle);
-    }
-
-    @Override
-    protected void onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
-        if (getPhoneNumberCallInteraction().onPrepareDialog(id, dialog, bundle)) {
-            return;
-        }
-
-        if (getSendTextMessageInteraction().onPrepareDialog(id, dialog, bundle)) {
-            return;
-        }
-
-        super.onPrepareDialog(id, dialog, bundle);
-    }
-
-    @Override
     protected void onActivityResult(int requestCode, int resultCode, Intent data) {
         switch (requestCode) {
             case SUBACTIVITY_CUSTOMIZE_FILTER: {
@@ -1088,20 +1059,6 @@
         }
     }
 
-    private PhoneNumberInteraction getPhoneNumberCallInteraction() {
-        if (mPhoneNumberCallInteraction == null) {
-            mPhoneNumberCallInteraction = new PhoneNumberInteraction(this, false, null);
-        }
-        return mPhoneNumberCallInteraction;
-    }
-
-    private PhoneNumberInteraction getSendTextMessageInteraction() {
-        if (mSendTextMessageInteraction == null) {
-            mSendTextMessageInteraction = new PhoneNumberInteraction(this, true, null);
-        }
-        return mSendTextMessageInteraction;
-    }
-
     @Override
     public DialogManager getDialogManager() {
         return mDialogManager;
diff --git a/src/com/android/contacts/interactions/PhoneNumberInteraction.java b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
index 399b0ea..8430559 100644
--- a/src/com/android/contacts/interactions/PhoneNumberInteraction.java
+++ b/src/com/android/contacts/interactions/PhoneNumberInteraction.java
@@ -16,11 +16,6 @@
 package com.android.contacts.interactions;
 
 
-import com.google.i18n.phonenumbers.NumberParseException;
-import com.google.i18n.phonenumbers.PhoneNumberUtil;
-import com.google.i18n.phonenumbers.PhoneNumberUtil.MatchType;
-import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
-
 import com.android.contacts.Collapser;
 import com.android.contacts.Collapser.Collapsible;
 import com.android.contacts.ContactSaveService;
@@ -29,15 +24,21 @@
 import com.android.contacts.model.AccountType.StringInflater;
 import com.android.contacts.model.AccountTypeManager;
 import com.android.contacts.model.DataKind;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.i18n.phonenumbers.NumberParseException;
+import com.google.i18n.phonenumbers.PhoneNumberUtil;
+import com.google.i18n.phonenumbers.PhoneNumberUtil.MatchType;
+import com.google.i18n.phonenumbers.Phonenumber.PhoneNumber;
 
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.FragmentManager;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.CursorLoader;
 import android.content.DialogInterface;
-import android.content.DialogInterface.OnClickListener;
 import android.content.DialogInterface.OnDismissListener;
 import android.content.Intent;
 import android.content.Loader;
@@ -57,45 +58,36 @@
 import android.view.ViewGroup;
 import android.widget.ArrayAdapter;
 import android.widget.CheckBox;
+import android.widget.ListAdapter;
 import android.widget.TextView;
 
 import java.util.ArrayList;
+import java.util.List;
 
 /**
- * Initiates phone calls or a text message.
+ * Initiates phone calls or a text message. If there are multiple candidates, this class shows a
+ * dialog to pick one.
  */
-public class PhoneNumberInteraction
-        implements OnLoadCompleteListener<Cursor>, OnClickListener {
+public class PhoneNumberInteraction implements OnLoadCompleteListener<Cursor> {
+    private static final String TAG = PhoneNumberInteraction.class.getSimpleName();
 
-    public static final String EXTRA_KEY_ITEMS = "items";
+    @VisibleForTesting
+    /* package */ enum InteractionType {
+        PHONE_CALL,
+        SMS
+    }
 
     /**
      * A model object for capturing a phone number for a given contact.
      */
-    static class PhoneItem implements Parcelable, Collapsible<PhoneItem> {
+    @VisibleForTesting
+    /* package */ static class PhoneItem implements Parcelable, Collapsible<PhoneItem> {
         long id;
         String phoneNumber;
         String accountType;
         long type;
         String label;
 
-        public static Parcelable.Creator<PhoneItem> CREATOR = new Creator<PhoneItem>() {
-
-            public PhoneItem[] newArray(int size) {
-                return new PhoneItem[size];
-            }
-
-            public PhoneItem createFromParcel(Parcel source) {
-                PhoneItem item = new PhoneItem();
-                item.id = source.readLong();
-                item.phoneNumber = source.readString();
-                item.accountType = source.readString();
-                item.type = source.readLong();
-                item.label = source.readString();
-                return item;
-            }
-        };
-
         public void writeToParcel(Parcel dest, int flags) {
             dest.writeLong(id);
             dest.writeString(phoneNumber);
@@ -140,30 +132,31 @@
     /**
      * A list adapter that populates the list of contact's phone numbers.
      */
-    private class PhoneItemAdapter extends ArrayAdapter<PhoneItem> {
-        private final AccountTypeManager mAccountTypes;
+    private static class PhoneItemAdapter extends ArrayAdapter<PhoneItem> {
+        private final InteractionType mInteractionType;
+        private final AccountTypeManager mAccountTypeManager;
 
-        public PhoneItemAdapter(Context context) {
-            super(context, R.layout.phone_disambig_item, android.R.id.text2);
-            mAccountTypes = AccountTypeManager.getInstance(context);
+        public PhoneItemAdapter(Context context, List<PhoneItem> list,
+                InteractionType interactionType) {
+            super(context, R.layout.phone_disambig_item, android.R.id.text2, list);
+            mInteractionType = interactionType;
+            mAccountTypeManager = AccountTypeManager.getInstance(context);
         }
 
         @Override
         public View getView(int position, View convertView, ViewGroup parent) {
-            View view = super.getView(position, convertView, parent);
+            final View view = super.getView(position, convertView, parent);
 
-            PhoneItem item = getItem(position);
-            AccountType accountType = mAccountTypes.getAccountType(item.accountType);
-
-            // Obtain a string representation of the phone type specific to the
-            // account type associated with that phone number
-            TextView typeView = (TextView)view.findViewById(android.R.id.text1);
-            DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
+            final PhoneItem item = getItem(position);
+            final AccountType accountType = mAccountTypeManager.getAccountType(item.accountType);
+            final TextView typeView = (TextView) view.findViewById(android.R.id.text1);
+            final DataKind kind = accountType.getKindForMimetype(Phone.CONTENT_ITEM_TYPE);
             if (kind != null) {
                 ContentValues values = new ContentValues();
                 values.put(Phone.TYPE, item.type);
                 values.put(Phone.LABEL, item.label);
-                StringInflater header = mSendTextMessage ? kind.actionAltHeader : kind.actionHeader;
+                StringInflater header = (mInteractionType == InteractionType.SMS)
+                        ? kind.actionAltHeader : kind.actionHeader;
                 typeView.setText(header.inflateUsing(getContext(), values));
             } else {
                 typeView.setText(R.string.call_other);
@@ -172,6 +165,73 @@
         }
     }
 
+    /**
+     * {@link DialogFragment} used for displaying a dialog with a list of phone numbers of which
+     * one will be chosen to make a call or initiate an sms message.
+     *
+     * It is recommended to use
+     * {@link PhoneNumberInteraction#startInteractionForPhoneCall(Activity, Uri)} or
+     * {@link PhoneNumberInteraction#startInteractionForTextMessage(Activity, Uri)} instead of
+     * directly using this class, as those methods handle one or multiple data cases appropriately.
+     */
+    /* Made public to let the system reach this class */
+    public static class PhoneDisambiguationDialogFragment extends DialogFragment
+            implements DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
+
+        private static final String ARG_PHONE_LIST = "phoneList";
+        private static final String ARG_INTERACTION_TYPE = "interactionType";
+
+        private InteractionType mInteractionType;
+        private ListAdapter mPhonesAdapter;
+        private List<PhoneItem> mPhoneList;
+
+        public static void show(FragmentManager fragmentManager,
+                ArrayList<PhoneItem> phoneList, InteractionType interactionType) {
+            PhoneDisambiguationDialogFragment fragment = new PhoneDisambiguationDialogFragment();
+            Bundle bundle = new Bundle();
+            bundle.putParcelableArrayList(ARG_PHONE_LIST, phoneList);
+            bundle.putSerializable(ARG_INTERACTION_TYPE, interactionType);
+            fragment.setArguments(bundle);
+            fragment.show(fragmentManager, TAG);
+        }
+
+        @Override
+        public Dialog onCreateDialog(Bundle savedInstanceState) {
+            final Activity activity = getActivity();
+            mPhoneList = getArguments().getParcelableArrayList(ARG_PHONE_LIST);
+            mInteractionType =
+                    (InteractionType) getArguments().getSerializable(ARG_INTERACTION_TYPE);
+            mPhonesAdapter = new PhoneItemAdapter(activity, mPhoneList, mInteractionType);
+            final LayoutInflater inflater = activity.getLayoutInflater();
+            final View setPrimaryView = inflater.inflate(R.layout.set_primary_checkbox, null);
+            return new AlertDialog.Builder(activity)
+                    .setAdapter(mPhonesAdapter, this)
+                    .setTitle(mInteractionType == InteractionType.SMS
+                            ? R.string.sms_disambig_title : R.string.call_disambig_title)
+                    .setView(setPrimaryView)
+                    .create();
+        }
+
+        public void onClick(DialogInterface dialog, int which) {
+            final AlertDialog alertDialog = (AlertDialog)dialog;
+            if (mPhoneList.size() > which && which >= 0) {
+                final PhoneItem phoneItem = mPhoneList.get(which);
+                final CheckBox checkBox = (CheckBox)alertDialog.findViewById(R.id.setPrimary);
+                if (checkBox.isChecked()) {
+                    // Request to mark the data as primary in the background.
+                    final Intent serviceIntent = ContactSaveService.createSetSuperPrimaryIntent(
+                            getActivity(), phoneItem.id);
+                    getActivity().startService(serviceIntent);
+                }
+
+                PhoneNumberInteraction.performAction(getActivity(), phoneItem.phoneNumber,
+                        mInteractionType);
+            } else {
+                dialog.dismiss();
+            }
+        }
+    }
+
     private static final String[] PHONE_NUMBER_PROJECTION = new String[] {
             Phone._ID,
             Phone.NUMBER,
@@ -186,28 +246,36 @@
 
     private final Context mContext;
     private final OnDismissListener mDismissListener;
-    private final boolean mSendTextMessage;
+    private final InteractionType mInteractionType;
 
     private CursorLoader mLoader;
 
-    public PhoneNumberInteraction(Context context, boolean sendTextMessage,
+    @VisibleForTesting
+    /* package */ PhoneNumberInteraction(Context context, InteractionType interactionType,
             DialogInterface.OnDismissListener dismissListener) {
         mContext = context;
-        mSendTextMessage = sendTextMessage;
+        mInteractionType = interactionType;
         mDismissListener = dismissListener;
     }
 
     private void performAction(String phoneNumber) {
-        Intent intent;
-        if (mSendTextMessage) {
-            intent = new Intent(
-                    Intent.ACTION_SENDTO, Uri.fromParts("sms", phoneNumber, null));
-        } else {
-            intent = new Intent(
-                    Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts("tel", phoneNumber, null));
+        PhoneNumberInteraction.performAction(mContext, phoneNumber, mInteractionType);
+    }
 
+    private static void performAction(
+            Context context, String phoneNumber, InteractionType interactionType) {
+        Intent intent;
+        switch (interactionType) {
+            case SMS:
+                intent = new Intent(
+                        Intent.ACTION_SENDTO, Uri.fromParts("sms", phoneNumber, null));
+                break;
+            default:
+                intent = new Intent(
+                        Intent.ACTION_CALL_PRIVILEGED, Uri.fromParts("tel", phoneNumber, null));
+                break;
         }
-        startActivity(intent);
+        context.startActivity(intent);
     }
 
     /**
@@ -274,9 +342,8 @@
             onDismiss();
             performAction(phoneList.get(0).phoneNumber);
         } else {
-            Bundle bundle = new Bundle();
-            bundle.putParcelableArrayList(EXTRA_KEY_ITEMS, phoneList);
-            showDialog(getDialogId(), bundle);
+            // There are multiple candidates. Let the user choose one.
+            showDisambiguationDialog(phoneList);
         }
     }
 
@@ -286,89 +353,33 @@
         }
     }
 
-    private int getDialogId() {
-        return mSendTextMessage
-                ? R.id.dialog_phone_number_message_disambiguation
-                : R.id.dialog_phone_number_call_disambiguation;
-    }
-
-    public Dialog onCreateDialog(int id, Bundle bundle) {
-        if (id != getDialogId()) {
-            return null;
-        }
-
-        LayoutInflater inflater = LayoutInflater.from(mContext);
-        View setPrimaryView = inflater.inflate(R.layout.set_primary_checkbox, null);
-        AlertDialog dialog = new AlertDialog.Builder(mContext)
-                .setAdapter(new PhoneItemAdapter(mContext), this)
-                .setView(setPrimaryView)
-                .setTitle(mSendTextMessage
-                        ? R.string.sms_disambig_title
-                        : R.string.call_disambig_title)
-                .create();
-        dialog.setOnDismissListener(mDismissListener);
-        return dialog;
-    }
-
-    public boolean onPrepareDialog(int id, Dialog dialog, Bundle bundle) {
-        if (id != getDialogId()) {
-            return false;
-        }
-
-        ArrayList<PhoneItem> phoneList = bundle.getParcelableArrayList(EXTRA_KEY_ITEMS);
-
-        AlertDialog alertDialog = (AlertDialog)dialog;
-        PhoneItemAdapter adapter = (PhoneItemAdapter)alertDialog.getListView().getAdapter();
-        adapter.clear();
-        adapter.addAll(phoneList);
-
-        return true;
+    /**
+     * Start call action using given contact Uri. If there are multiple candidates for the phone
+     * call, dialog is automatically shown and the user is asked to choose one.
+     */
+    public static void startInteractionForPhoneCall(Activity activity, Uri contactUri) {
+        (new PhoneNumberInteraction(activity, InteractionType.PHONE_CALL, null))
+                .startInteraction(contactUri);
     }
 
     /**
-     * Handles the user selection in the disambiguation dialog.
+     * Start text messaging (a.k.a SMS) action using given contact Uri. If there are multiple
+     * candidates for the phone call, dialog is automatically shown and the user is asked to choose
+     * one.
      */
-    @Override
-    public void onClick(DialogInterface dialog, int which) {
-        AlertDialog alertDialog = (AlertDialog)dialog;
-        PhoneItemAdapter adapter = (PhoneItemAdapter)alertDialog.getListView().getAdapter();
-        PhoneItem phoneItem = adapter.getItem(which);
-        if (phoneItem != null) {
-            long id = phoneItem.id;
-            String phone = phoneItem.phoneNumber;
-
-            CheckBox checkBox = (CheckBox)alertDialog.findViewById(R.id.setPrimary);
-            if (checkBox.isChecked()) {
-                makePrimary(id);
-            }
-
-            performAction(phone);
-        }
+    public static void startInteractionForTextMessage(Activity activity, Uri contactUri) {
+        (new PhoneNumberInteraction(activity, InteractionType.SMS, null))
+                .startInteraction(contactUri);
     }
 
-    /**
-     * Makes the selected phone number primary.
-     */
-    void makePrimary(long id) {
-        final Intent intent = ContactSaveService.createSetSuperPrimaryIntent(mContext, id);
-        mContext.startService(intent);
-    }
-
-    /* Visible for testing */
-    void showDialog(int dialogId, Bundle bundle) {
-        Activity activity = (Activity)mContext;
-        if (!activity.isFinishing()) {
-            activity.showDialog(dialogId, bundle);
-        }
-    }
-
-    /* Visible for testing */
-    void startActivity(Intent intent) {
-        mContext.startActivity(intent);
-    }
-
-    /* Visible for testing */
+    @VisibleForTesting
     CursorLoader getLoader() {
         return mLoader;
     }
+
+    @VisibleForTesting
+    void showDisambiguationDialog(ArrayList<PhoneItem> phoneList) {
+        PhoneDisambiguationDialogFragment.show(((Activity)mContext).getFragmentManager(),
+                phoneList, mInteractionType);
+    }
 }