Merge "Add call shortcuts  to phone number list adapters" into klp-dev
diff --git a/res/drawable-hdpi/ic_add_person_dk.png b/res/drawable-hdpi/ic_add_person_dk.png
new file mode 100644
index 0000000..7096ed2
--- /dev/null
+++ b/res/drawable-hdpi/ic_add_person_dk.png
Binary files differ
diff --git a/res/drawable-hdpi/ic_phone_dk.png b/res/drawable-hdpi/ic_phone_dk.png
new file mode 100644
index 0000000..984d6fa
--- /dev/null
+++ b/res/drawable-hdpi/ic_phone_dk.png
Binary files differ
diff --git a/res/drawable-hdpi/list_item_avatar_bg.png b/res/drawable-hdpi/list_item_avatar_bg.png
new file mode 100644
index 0000000..e6eb68e
--- /dev/null
+++ b/res/drawable-hdpi/list_item_avatar_bg.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_add_person_dk.png b/res/drawable-mdpi/ic_add_person_dk.png
new file mode 100644
index 0000000..0e0aed0
--- /dev/null
+++ b/res/drawable-mdpi/ic_add_person_dk.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_phone_dk.png b/res/drawable-mdpi/ic_phone_dk.png
new file mode 100644
index 0000000..c55571a
--- /dev/null
+++ b/res/drawable-mdpi/ic_phone_dk.png
Binary files differ
diff --git a/res/drawable-mdpi/list_item_avatar_bg.png b/res/drawable-mdpi/list_item_avatar_bg.png
new file mode 100644
index 0000000..1306cfc
--- /dev/null
+++ b/res/drawable-mdpi/list_item_avatar_bg.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_add_person_dk.png b/res/drawable-xhdpi/ic_add_person_dk.png
new file mode 100644
index 0000000..5211f5c
--- /dev/null
+++ b/res/drawable-xhdpi/ic_add_person_dk.png
Binary files differ
diff --git a/res/drawable-xhdpi/ic_phone_dk.png b/res/drawable-xhdpi/ic_phone_dk.png
new file mode 100644
index 0000000..74a7032
--- /dev/null
+++ b/res/drawable-xhdpi/ic_phone_dk.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_item_avatar_bg.png b/res/drawable-xhdpi/list_item_avatar_bg.png
new file mode 100644
index 0000000..3f271a0
--- /dev/null
+++ b/res/drawable-xhdpi/list_item_avatar_bg.png
Binary files differ
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 65746bb..c6947dd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -612,4 +612,12 @@
     <string name="favorite_hidden">Hidden from favorites</string>
     <!--  Text displayed for the undo button to undo removing a favorite contact -->
     <string name="favorite_hidden_undo">Undo</string>
+
+    <!-- Shortcut item used to call a number directly from search -->
+    <string name="search_shortcut_call_number">Call
+        <xliff:g id="number">%s</xliff:g>
+    </string>
+
+    <!-- Shortcut item used to add a number directly from search -->
+    <string name="search_shortcut_add_to_contacts">Add to contacts</string>
 </resources>
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 398c90e..c85ce42 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -37,6 +37,7 @@
 import android.provider.CallLog.Calls;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Intents;
 import android.provider.ContactsContract.Intents.UI;
 import android.provider.Settings;
 import android.speech.RecognizerIntent;
@@ -882,4 +883,17 @@
         final Intent intent = new Intent(this, AllContactsActivity.class);
         startActivity(intent);
     }
+
+    public static Intent getAddNumberToContactIntent(CharSequence text) {
+        final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
+        intent.putExtra(Intents.Insert.PHONE, text);
+        intent.setType(Contacts.CONTENT_ITEM_TYPE);
+        return intent;
+    }
+
+    public static Intent getInsertContactWithNameIntent(CharSequence text) {
+        final Intent intent = new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI);
+        intent.putExtra(Intents.Insert.NAME, text);
+        return intent;
+    }
 }
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 361f60d..c976a9e 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -36,10 +36,11 @@
 import android.os.RemoteException;
 import android.os.ServiceManager;
 import android.os.SystemProperties;
-import android.provider.Contacts.Intents.Insert;
+import android.provider.ContactsContract.Contacts;
 import android.provider.Contacts.People;
 import android.provider.Contacts.Phones;
 import android.provider.Contacts.PhonesColumns;
+import android.provider.ContactsContract.Intents;
 import android.provider.Settings;
 import android.telephony.PhoneNumberUtils;
 import android.telephony.PhoneStateListener;
@@ -793,18 +794,11 @@
         } else {
             final CharSequence digits = mDigits.getText();
             // Put the current digits string into an intent
-            addToContactMenuItem.setIntent(getAddToContactIntent(digits));
+            addToContactMenuItem.setIntent(DialtactsActivity.getAddNumberToContactIntent(digits));
             addToContactMenuItem.setVisible(true);
         }
     }
 
-    private static Intent getAddToContactIntent(CharSequence digits) {
-        final Intent intent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
-        intent.putExtra(Insert.PHONE, digits);
-        intent.setType(People.CONTENT_ITEM_TYPE);
-        return intent;
-    }
-
     private void keyPressed(int keyCode) {
         switch (keyCode) {
             case KeyEvent.KEYCODE_1:
diff --git a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java b/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
index f558e37..a13ecd8 100644
--- a/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
+++ b/src/com/android/dialer/dialpad/SmartDialCursorLoader.java
@@ -59,11 +59,10 @@
         if (DEBUG) {
             Log.v(TAG, "Configure new query to be " + query);
         }
-        mQuery = query;
+        mQuery = SmartDialNameMatcher.normalizeNumber(query, SmartDialPrefix.getMap());
 
         /** Constructs a name matcher object for matching names. */
-        mNameMatcher = new SmartDialNameMatcher(PhoneNumberUtils.normalizeNumber(query),
-            SmartDialPrefix.getMap());
+        mNameMatcher = new SmartDialNameMatcher(mQuery, SmartDialPrefix.getMap());
     }
 
     /**
diff --git a/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java b/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
new file mode 100644
index 0000000..e911710
--- /dev/null
+++ b/src/com/android/dialer/list/DialerPhoneNumberListAdapter.java
@@ -0,0 +1,188 @@
+package com.android.dialer.list;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.telephony.PhoneNumberUtils;
+import android.text.TextUtils;
+import android.view.View;
+import android.view.ViewGroup;
+
+import com.android.contacts.common.GeoUtil;
+import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.PhoneNumberListAdapter;
+import com.android.dialer.R;
+
+/**
+ * {@link PhoneNumberListAdapter} with the following added shortcuts, that are displayed as list
+ * items:
+ * 1) Directly calling the phone number query
+ * 2) Adding the phone number query to a contact
+ *
+ * These shortcuts can be enabled or disabled to toggle whether or not they show up in the
+ * list.
+ */
+public class DialerPhoneNumberListAdapter extends PhoneNumberListAdapter {
+
+    private String mFormattedQueryString;
+    private String mCountryIso;
+
+    public final static int SHORTCUT_INVALID = -1;
+    public final static int SHORTCUT_DIRECT_CALL = 0;
+    public final static int SHORTCUT_ADD_NUMBER_TO_CONTACTS = 1;
+    public final static int SHORTCUT_ADD_NEW_NAMED_CONTACT = 2;
+
+    public final static int SHORTCUT_COUNT = 3;
+
+    private final boolean[] mShortcutEnabled = new boolean[SHORTCUT_COUNT];
+
+    public DialerPhoneNumberListAdapter(Context context) {
+        super(context);
+
+        mCountryIso = GeoUtil.getCurrentCountryIso(context);
+
+        // Enable all shortcuts by default
+        for (int i = 0; i < mShortcutEnabled.length; i++) {
+            mShortcutEnabled[i] = true;
+        }
+    }
+
+    @Override
+    public int getCount() {
+        return super.getCount() + getShortcutCount();
+    }
+
+    /**
+     * @return The number of enabled shortcuts. Ranges from 0 to a maximum of SHORTCUT_COUNT
+     */
+    public int getShortcutCount() {
+        int count = 0;
+        for (int i = 0; i < mShortcutEnabled.length; i++) {
+            if (mShortcutEnabled[i]) count++;
+        }
+        return count;
+    }
+
+    @Override
+    public int getItemViewType(int position) {
+        final int shortcut = getShortcutTypeFromPosition(position);
+        if (shortcut >= 0) {
+            // shortcutPos should always range from 1 to SHORTCUT_COUNT
+            return super.getViewTypeCount() + shortcut;
+        } else {
+            return super.getItemViewType(position);
+        }
+    }
+
+    @Override
+    public int getViewTypeCount() {
+        // Number of item view types in the super implementation + 2 for the 2 new shortcuts
+        return super.getViewTypeCount() + SHORTCUT_COUNT;
+    }
+
+    @Override
+    public View getView(int position, View convertView, ViewGroup parent) {
+        final int shortcutType = getShortcutTypeFromPosition(position);
+        if (shortcutType >= 0) {
+            if (convertView != null) {
+                assignShortcutToView((ContactListItemView) convertView, shortcutType);
+                return convertView;
+            } else {
+                final ContactListItemView v = new ContactListItemView(getContext(), null);
+                assignShortcutToView(v, shortcutType);
+                return v;
+            }
+        } else {
+            return super.getView(position, convertView, parent);
+        }
+    }
+
+    /**
+     * @param position The position of the item
+     * @return The enabled shortcut type matching the given position if the item is a
+     * shortcut, -1 otherwise
+     */
+    public int getShortcutTypeFromPosition(int position) {
+        int shortcutCount = position - super.getCount();
+        if (shortcutCount >= 0) {
+            // Iterate through the array of shortcuts, looking only for shortcuts where
+            // mShortcutEnabled[i] is true
+            for (int i = 0; shortcutCount >= 0 && i < mShortcutEnabled.length; i++) {
+                if (mShortcutEnabled[i]) {
+                    shortcutCount--;
+                    if (shortcutCount < 0) return i;
+                }
+            }
+            throw new IllegalArgumentException("Invalid position - greater than cursor count "
+                    + " but not a shortcut.");
+        }
+        return SHORTCUT_INVALID;
+    }
+
+    @Override
+    public boolean isEmpty() {
+        return getShortcutCount() == 0 && super.isEmpty();
+    }
+
+    @Override
+    public boolean isEnabled(int position) {
+        final int shortcutType = getShortcutTypeFromPosition(position);
+        if (shortcutType >= 0) {
+            return true;
+        } else {
+            return super.isEnabled(position);
+        }
+    }
+
+    private void assignShortcutToView(ContactListItemView v, int shortcutType) {
+        final CharSequence text;
+        final int drawableId;
+        final Resources resources = getContext().getResources();
+        final String number = getFormattedQueryString();
+        switch (shortcutType) {
+            case SHORTCUT_DIRECT_CALL:
+                text = resources.getString(R.string.search_shortcut_call_number, number);
+                drawableId = R.drawable.ic_phone_dk;
+                break;
+            case SHORTCUT_ADD_NUMBER_TO_CONTACTS:
+                text = resources.getString(R.string.search_shortcut_add_to_contacts);
+                drawableId = R.drawable.ic_add_person_dk;
+                break;
+            case SHORTCUT_ADD_NEW_NAMED_CONTACT:
+                text = resources.getString(R.string.search_shortcut_add_to_contacts);
+                drawableId = R.drawable.ic_add_person_dk;
+                break;
+            default:
+                throw new IllegalArgumentException("Invalid shortcut type");
+        }
+        v.setDrawableResource(R.drawable.list_item_avatar_bg, drawableId);
+        v.setDisplayName(text);
+        v.setPhotoPosition(super.getPhotoPosition());
+    }
+
+    public void setShortcutEnabled(int shortcutType, boolean visible) {
+        mShortcutEnabled[shortcutType] = visible;
+    }
+
+    public String getFormattedQueryString() {
+        return mFormattedQueryString;
+    }
+
+    @Override
+    public void setQueryString(String queryString) {
+        boolean containsNonDialableCharacters = false;
+        for (int i = 0; i < queryString.length(); i++) {
+            if (!PhoneNumberUtils.isDialable(queryString.charAt(i))) {
+                containsNonDialableCharacters = true;
+                break;
+            }
+        }
+
+        if (containsNonDialableCharacters) {
+            mFormattedQueryString = null;
+        } else {
+            mFormattedQueryString = PhoneNumberUtils.formatNumber(queryString, mCountryIso);
+        }
+
+        super.setQueryString(queryString);
+    }
+}
diff --git a/src/com/android/dialer/list/RegularSearchListAdapter.java b/src/com/android/dialer/list/RegularSearchListAdapter.java
index 390dbb5..627262e 100644
--- a/src/com/android/dialer/list/RegularSearchListAdapter.java
+++ b/src/com/android/dialer/list/RegularSearchListAdapter.java
@@ -19,6 +19,7 @@
 import android.database.Cursor;
 import android.net.Uri;
 import android.provider.ContactsContract;
+import android.text.TextUtils;
 
 import com.android.contacts.common.list.DirectoryPartition;
 import com.android.contacts.common.list.PhoneNumberListAdapter;
@@ -27,7 +28,7 @@
 /**
  * List adapter to display regular search results.
  */
-public class RegularSearchListAdapter extends PhoneNumberListAdapter {
+public class RegularSearchListAdapter extends DialerPhoneNumberListAdapter {
 
     public RegularSearchListAdapter(Context context) {
         super(context);
@@ -59,4 +60,16 @@
         }
         return info;
     }
+
+    @Override
+    public void setQueryString(String queryString) {
+        final boolean showNumberShortcuts = !TextUtils.isEmpty(getFormattedQueryString());
+        setShortcutEnabled(SHORTCUT_DIRECT_CALL, showNumberShortcuts);
+        // Either one of the add contacts options should be enabled. If the user entered
+        // a dialable number, then clicking add to contact should add it as a number.
+        // Otherwise, it should add it to a new contact as a name.
+        setShortcutEnabled(SHORTCUT_ADD_NUMBER_TO_CONTACTS, showNumberShortcuts);
+        setShortcutEnabled(SHORTCUT_ADD_NEW_NAMED_CONTACT, !showNumberShortcuts);
+        super.setQueryString(queryString);
+    }
 }
diff --git a/src/com/android/dialer/list/SearchFragment.java b/src/com/android/dialer/list/SearchFragment.java
index 54ee436..0302d97 100644
--- a/src/com/android/dialer/list/SearchFragment.java
+++ b/src/com/android/dialer/list/SearchFragment.java
@@ -16,12 +16,19 @@
 package com.android.dialer.list;
 
 import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
 import android.widget.AbsListView;
 import android.widget.AbsListView.OnScrollListener;
+import android.widget.Toast;
 
 import com.android.contacts.common.list.ContactEntryListAdapter;
 import com.android.contacts.common.list.ContactListItemView;
+import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
 import com.android.contacts.common.list.PhoneNumberPickerFragment;
+import com.android.dialer.DialtactsActivity;
+import com.android.dialer.R;
+import com.android.dialer.dialpad.DialpadFragment;
 import com.android.dialer.list.OnListFragmentScrolledListener;
 
 public class SearchFragment extends PhoneNumberPickerFragment {
@@ -73,4 +80,46 @@
             adapter.setHasHeader(0, false);
         }
     }
+
+    @Override
+    protected ContactEntryListAdapter createListAdapter() {
+        DialerPhoneNumberListAdapter adapter = new DialerPhoneNumberListAdapter(getActivity());
+        adapter.setDisplayPhotos(true);
+        adapter.setUseCallableUri(super.usesCallableUri());
+        return adapter;
+    }
+
+    @Override
+    protected void onItemClick(int position, long id) {
+        final DialerPhoneNumberListAdapter adapter = (DialerPhoneNumberListAdapter) getAdapter();
+        final int shortcutType = adapter.getShortcutTypeFromPosition(position);
+
+        if (shortcutType == DialerPhoneNumberListAdapter.SHORTCUT_INVALID) {
+            super.onItemClick(position, id);
+        } else if (shortcutType == DialerPhoneNumberListAdapter.SHORTCUT_DIRECT_CALL) {
+            final OnPhoneNumberPickerActionListener listener =
+                    getOnPhoneNumberPickerListener();
+            if (listener != null) {
+                listener.onCallNumberDirectly(getQueryString());
+            }
+        } else if (shortcutType == DialerPhoneNumberListAdapter.SHORTCUT_ADD_NUMBER_TO_CONTACTS) {
+            final String number = adapter.getFormattedQueryString();
+            final Intent intent = DialtactsActivity.getAddNumberToContactIntent(number);
+            startActivityWithErrorToast(intent);
+        } else if (shortcutType == DialerPhoneNumberListAdapter.SHORTCUT_ADD_NEW_NAMED_CONTACT) {
+            final String name = adapter.getQueryString();
+            final Intent intent = DialtactsActivity.getInsertContactWithNameIntent(name);
+            startActivityWithErrorToast(intent);
+        }
+    }
+
+    private void startActivityWithErrorToast(Intent intent) {
+        try {
+            startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Toast toast = Toast.makeText(getActivity(), R.string.add_contact_not_available,
+                    Toast.LENGTH_SHORT);
+            toast.show();
+        }
+    }
 }
diff --git a/src/com/android/dialer/list/SmartDialNumberListAdapter.java b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
index c5ce59a..962d8ee 100644
--- a/src/com/android/dialer/list/SmartDialNumberListAdapter.java
+++ b/src/com/android/dialer/list/SmartDialNumberListAdapter.java
@@ -37,7 +37,7 @@
 /**
  * List adapter to display the SmartDial search results.
  */
-public class SmartDialNumberListAdapter extends PhoneNumberListAdapter{
+public class SmartDialNumberListAdapter extends DialerPhoneNumberListAdapter {
 
     private static final String TAG = SmartDialNumberListAdapter.class.getSimpleName();
     private static final boolean DEBUG = false;
diff --git a/src/com/android/dialer/list/SmartDialSearchFragment.java b/src/com/android/dialer/list/SmartDialSearchFragment.java
index a163f5d..4248c85 100644
--- a/src/com/android/dialer/list/SmartDialSearchFragment.java
+++ b/src/com/android/dialer/list/SmartDialSearchFragment.java
@@ -38,6 +38,11 @@
         SmartDialNumberListAdapter adapter = new SmartDialNumberListAdapter(getActivity());
         adapter.setUseCallableUri(super.usesCallableUri());
         adapter.setQuickContactEnabled(true);
+        // Disable the direct call shortcut for the smart dial fragment, since the call button
+        // will already be showing anyway.
+        adapter.setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_DIRECT_CALL, false);
+        adapter.setShortcutEnabled(SmartDialNumberListAdapter.SHORTCUT_ADD_NEW_NAMED_CONTACT,
+                false);
         return adapter;
     }