Restoring some of the phone number picker with Loaders and Fragments

Also, adding support for pictures and section headers in
the phone picker.

Change-Id: Icc86442c6b45a942998204f2d6e8e3a5aff70de0
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 160bacc..2c15f72 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -28,6 +28,8 @@
 import com.android.contacts.list.MultiplePhonePickerFragment;
 import com.android.contacts.list.OnContactBrowserActionListener;
 import com.android.contacts.list.OnContactPickerActionListener;
+import com.android.contacts.list.OnPhoneNumberPickerActionListener;
+import com.android.contacts.list.PhoneNumberPickerFragment;
 import com.android.contacts.list.StrequentContactListFragment;
 import com.android.contacts.model.ContactsSource;
 import com.android.contacts.model.Sources;
@@ -686,9 +688,25 @@
             case MODE_LEGACY_PICK_PHONE:
             case MODE_PICK_PHONE: {
                 mListFragment = new DefaultContactListFragment();
+                PhoneNumberPickerFragment fragment = new PhoneNumberPickerFragment();
                 if (mMode == MODE_LEGACY_PICK_PHONE) {
-                    mListFragment.setLegacyCompatibility(true);
+                    fragment.setLegacyCompatibility(true);
                 }
+                fragment.setSectionHeaderDisplayEnabled(false);
+                fragment.setOnPhoneNumberPickerActionListener(
+                        new OnPhoneNumberPickerActionListener() {
+
+                    public void onPickPhoneNumberAction(Uri dataUri) {
+                        Intent intent = new Intent();
+                        setResult(RESULT_OK, intent.setData(dataUri));
+                        finish();
+                    }
+
+                    public void onSearchAllContactsAction(String string) {
+                        doSearch();
+                    }
+                });
+                mListFragment = fragment;
                 break;
             }
             case MODE_LEGACY_PICK_POSTAL:
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 3ccd973..0316251 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -16,6 +16,7 @@
 
 package com.android.contacts.list;
 
+import com.android.contacts.ContactEntryListView;
 import com.android.contacts.ContactPhotoLoader;
 import com.android.contacts.ContactsApplicationController;
 import com.android.contacts.ContactsListActivity;
@@ -31,6 +32,7 @@
 import android.database.Cursor;
 import android.os.Bundle;
 import android.os.Parcelable;
+import android.provider.ContactsContract;
 import android.text.Editable;
 import android.text.Html;
 import android.text.TextUtils;
@@ -112,6 +114,10 @@
         if (isPhotoLoaderEnabled()) {
             mAdapter.setPhotoLoader(mPhotoLoader);
         }
+        if (isNameHighlighingEnabled()) {
+            mAdapter.setNameHighlightingEnabled(true);
+        }
+
         ((ContactsListActivity)getActivity()).setupListView(mAdapter, mListView);
     }
 
@@ -295,6 +301,9 @@
             mListView.setOnCreateContextMenuListener(mContextMenuAdapter);
         }
 
+        ContactEntryListView listView = (ContactEntryListView)mListView;
+        listView.setHighlightNamesWhenScrolling(isNameHighlighingEnabled());
+
         if (isPhotoLoaderEnabled()) {
             mPhotoLoader =
                 new ContactPhotoLoader(getActivity(), R.drawable.ic_contact_list_picture);
@@ -319,6 +328,20 @@
         return mView;
     }
 
+    private boolean isNameHighlighingEnabled() {
+        // When sort order and display order contradict each other, we want to
+        // highlight the part of the name used for sorting.
+        if (mSortOrder == ContactsContract.Preferences.SORT_ORDER_PRIMARY &&
+                mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_ALTERNATIVE) {
+            return true;
+        } else if (mSortOrder == ContactsContract.Preferences.SORT_ORDER_ALTERNATIVE &&
+                mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+            return true;
+        } else {
+            return false;
+        }
+    }
+
     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
             int totalItemCount) {
     }
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 50a3f4b..2aa9627 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -67,7 +67,6 @@
         adapter.setDisplayPhotos(true);
         adapter.setQuickContactEnabled(true);
 
-        adapter.configureLoader(getLoader());
         return adapter;
     }
 
diff --git a/src/com/android/contacts/list/OnPhoneNumberPickerActionListener.java b/src/com/android/contacts/list/OnPhoneNumberPickerActionListener.java
new file mode 100644
index 0000000..f30c223
--- /dev/null
+++ b/src/com/android/contacts/list/OnPhoneNumberPickerActionListener.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 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.contacts.list;
+
+import android.net.Uri;
+
+/**
+ * Action callbacks that can be sent by a phone number picker.
+ */
+public interface OnPhoneNumberPickerActionListener  {
+
+    /**
+     * Returns the selected phone number to the requester.
+     */
+    void onPickPhoneNumberAction(Uri dataUri);
+
+    /**
+     * Searches all contacts for the specified string an show results for browsing.
+     */
+    void onSearchAllContactsAction(String string);
+}
diff --git a/src/com/android/contacts/list/PhoneNumberListAdapter.java b/src/com/android/contacts/list/PhoneNumberListAdapter.java
new file mode 100644
index 0000000..70ec4ec
--- /dev/null
+++ b/src/com/android/contacts/list/PhoneNumberListAdapter.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2010 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.contacts.list;
+
+import android.app.patterns.CursorLoader;
+import android.content.ContentUris;
+import android.content.Context;
+import android.database.Cursor;
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.ContactCounts;
+import android.provider.ContactsContract.Data;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * A cursor adapter for the {@link Phone#CONTENT_TYPE} content type.
+ */
+public class PhoneNumberListAdapter extends ContactEntryListAdapter {
+
+    protected static final String[] PHONES_PROJECTION = new String[] {
+        Phone._ID,                          // 0
+        Phone.TYPE,                         // 1
+        Phone.LABEL,                        // 2
+        Phone.NUMBER,                       // 3
+        Phone.DISPLAY_NAME_PRIMARY,         // 4
+        Phone.DISPLAY_NAME_ALTERNATIVE,     // 5
+        Phone.CONTACT_ID,                   // 6
+        Phone.PHOTO_ID,                     // 7
+        Phone.PHONETIC_NAME,                // 8
+    };
+
+    protected static final int PHONE_ID_COLUMN_INDEX = 0;
+    protected static final int PHONE_TYPE_COLUMN_INDEX = 1;
+    protected static final int PHONE_LABEL_COLUMN_INDEX = 2;
+    protected static final int PHONE_NUMBER_COLUMN_INDEX = 3;
+    protected static final int PHONE_PRIMARY_DISPLAY_NAME_COLUMN_INDEX = 4;
+    protected static final int PHONE_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX = 5;
+    protected static final int PHONE_CONTACT_ID_COLUMN_INDEX = 6;
+    protected static final int PHONE_PHOTO_ID_COLUMN_INDEX = 7;
+    protected static final int PHONE_PHONETIC_NAME_COLUMN_INDEX = 8;
+
+    private CharSequence mUnknownNameText;
+    private int mDisplayNameColumnIndex;
+    private int mAlternativeDisplayNameColumnIndex;
+
+    public PhoneNumberListAdapter(Context context) {
+        super(context);
+
+        mUnknownNameText = context.getText(android.R.string.unknownName);
+    }
+
+    @Override
+    public void configureLoader(CursorLoader loader) {
+        loader.setUri(buildSectionIndexerUri(Phone.CONTENT_URI));
+        loader.setProjection(PHONES_PROJECTION);
+
+        if (getSortOrder() == ContactsContract.Preferences.SORT_ORDER_PRIMARY) {
+            loader.setSortOrder(Phone.SORT_KEY_PRIMARY);
+        } else {
+            loader.setSortOrder(Phone.SORT_KEY_ALTERNATIVE);
+        }
+    }
+
+    protected static Uri buildSectionIndexerUri(Uri uri) {
+        return uri.buildUpon()
+                .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
+    }
+
+    @Override
+    public String getContactDisplayName() {
+        return getCursor().getString(mDisplayNameColumnIndex);
+    }
+
+    @Override
+    public void setContactNameDisplayOrder(int displayOrder) {
+        super.setContactNameDisplayOrder(displayOrder);
+        if (getContactNameDisplayOrder() == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
+            mDisplayNameColumnIndex = PHONE_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+            mAlternativeDisplayNameColumnIndex = PHONE_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+        } else {
+            mDisplayNameColumnIndex = PHONE_ALTERNATIVE_DISPLAY_NAME_COLUMN_INDEX;
+            mAlternativeDisplayNameColumnIndex = PHONE_PRIMARY_DISPLAY_NAME_COLUMN_INDEX;
+        }
+    }
+
+    /**
+     * Builds a {@link Data#CONTENT_URI} for the current cursor
+     * position.
+     */
+    public Uri getDataUri() {
+        Cursor cursor = getCursor();
+        long id = cursor.getLong(PHONE_ID_COLUMN_INDEX);
+        return ContentUris.withAppendedId(Data.CONTENT_URI, id);
+    }
+
+    @Override
+    public View newView(Context context, Cursor cursor, ViewGroup parent) {
+        final ContactListItemView view = new ContactListItemView(context, null);
+        view.setUnknownNameText(mUnknownNameText);
+        view.setTextWithHighlightingFactory(getTextWithHighlightingFactory());
+        return view;
+    }
+
+    @Override
+    public void bindView(View itemView, Context context, Cursor cursor) {
+        ContactListItemView view = (ContactListItemView)itemView;
+        bindSectionHeaderAndDivider(view, cursor);
+        bindName(view, cursor);
+        bindPhoto(view, cursor);
+        bindPhoneNumber(view, cursor);
+    }
+
+    protected void bindPhoneNumber(ContactListItemView view, Cursor cursor) {
+        CharSequence label = null;
+        if (!cursor.isNull(PHONE_TYPE_COLUMN_INDEX)) {
+            final int type = cursor.getInt(PHONE_TYPE_COLUMN_INDEX);
+            final String customLabel = cursor.getString(PHONE_LABEL_COLUMN_INDEX);
+
+            // TODO cache
+            label = Phone.getTypeLabel(getContext().getResources(), type, customLabel);
+        }
+        view.setLabel(label);
+        view.showData(cursor, PHONE_NUMBER_COLUMN_INDEX);
+    }
+
+    protected void bindSectionHeaderAndDivider(final ContactListItemView view, Cursor cursor) {
+        final int position = cursor.getPosition();
+        final int section = getSectionForPosition(position);
+        if (getPositionForSection(section) == position) {
+            String title = (String)getSections()[section];
+            view.setSectionHeader(title);
+        } else {
+            view.setDividerVisible(false);
+            view.setSectionHeader(null);
+        }
+
+        // move the divider for the last item in a section
+        if (getPositionForSection(section + 1) - 1 == position) {
+            view.setDividerVisible(false);
+        } else {
+            view.setDividerVisible(true);
+        }
+    }
+
+    protected void bindName(final ContactListItemView view, Cursor cursor) {
+        view.showDisplayName(cursor, mDisplayNameColumnIndex, isNameHighlightingEnabled(),
+                mAlternativeDisplayNameColumnIndex);
+        view.showPhoneticName(cursor, PHONE_PHONETIC_NAME_COLUMN_INDEX);
+    }
+
+    protected void bindPhoto(final ContactListItemView view, Cursor cursor) {
+        long photoId = 0;
+        if (!cursor.isNull(PHONE_PHOTO_ID_COLUMN_INDEX)) {
+            photoId = cursor.getLong(PHONE_PHOTO_ID_COLUMN_INDEX);
+        }
+
+        getPhotoLoader().loadPhoto(view.getPhotoView(), photoId);
+    }
+//
+//    protected void bindSearchSnippet(final ContactListItemView view, Cursor cursor) {
+//        view.showSnippet(cursor, SUMMARY_SNIPPET_MIMETYPE_COLUMN_INDEX,
+//                SUMMARY_SNIPPET_DATA1_COLUMN_INDEX, SUMMARY_SNIPPET_DATA4_COLUMN_INDEX);
+//    }
+
+}
diff --git a/src/com/android/contacts/list/PhoneNumberPickerFragment.java b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
new file mode 100644
index 0000000..588f4ac
--- /dev/null
+++ b/src/com/android/contacts/list/PhoneNumberPickerFragment.java
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2010 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.contacts.list;
+
+import com.android.contacts.R;
+
+import android.net.Uri;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+
+/**
+ * Fragment containing a contact list used for browsing (as compared to
+ * picking a contact with one of the PICK intents).
+ */
+public class PhoneNumberPickerFragment extends ContactEntryListFragment<PhoneNumberListAdapter> {
+    private OnPhoneNumberPickerActionListener mListener;
+
+    public PhoneNumberPickerFragment() {
+        setPhotoLoaderEnabled(true);
+        setSectionHeaderDisplayEnabled(true);
+    }
+
+    public void setOnPhoneNumberPickerActionListener(OnPhoneNumberPickerActionListener listener) {
+        this.mListener = listener;
+    }
+
+    @Override
+    protected void onItemClick(int position, long id) {
+        PhoneNumberListAdapter adapter = getAdapter();
+//        if (adapter.isSearchAllContactsItemPosition(position)) {
+//            searchAllContacts();
+//        } else {
+        adapter.moveToPosition(position);
+        pickPhoneNumber(adapter.getDataUri());
+//        }
+    }
+
+    @Override
+    protected PhoneNumberListAdapter createListAdapter() {
+        PhoneNumberListAdapter adapter = new PhoneNumberListAdapter(getActivity());
+
+        adapter.setSectionHeaderDisplayEnabled(true);
+        adapter.setDisplayPhotos(true);
+
+        adapter.setSearchMode(isSearchMode());
+        adapter.setSearchResultsMode(isSearchResultsMode());
+        adapter.setQueryString(getQueryString());
+
+        adapter.setContactNameDisplayOrder(getContactNameDisplayOrder());
+        adapter.setSortOrder(getSortOrder());
+
+        return adapter;
+    }
+
+    @Override
+    protected View inflateView(LayoutInflater inflater, ViewGroup container) {
+        if (isSearchMode()) {
+            return inflater.inflate(R.layout.contacts_search_content, null);
+        } else if (isSearchResultsMode()) {
+            return inflater.inflate(R.layout.contacts_list_search_results, null);
+        } else {
+            return inflater.inflate(R.layout.contacts_list_content, null);
+        }
+    }
+
+    public void pickPhoneNumber(Uri uri) {
+        mListener.onPickPhoneNumberAction(uri);
+    }
+}
diff --git a/src/com/android/contacts/list/StrequentContactListFragment.java b/src/com/android/contacts/list/StrequentContactListFragment.java
index fac741b..f678aca 100644
--- a/src/com/android/contacts/list/StrequentContactListFragment.java
+++ b/src/com/android/contacts/list/StrequentContactListFragment.java
@@ -42,15 +42,15 @@
         StrequentContactListAdapter adapter =
                 new StrequentContactListAdapter(getActivity(), CALL_BUTTON_ID);
         adapter.setSectionHeaderDisplayEnabled(false);
+        adapter.setDisplayPhotos(true);
 
         adapter.setContactNameDisplayOrder(getContactNameDisplayOrder());
         adapter.setSortOrder(getSortOrder());
 
-        adapter.setDisplayPhotos(true);
         adapter.setQuickContactEnabled(true);
 
         adapter.setCallButtonListener(this);
-        adapter.configureLoader(getLoader());
+
         return adapter;
     }