Fixing issues with selection preservation including search

We have to do quite bit of work here because contact IDs
are unstable.

Change-Id: I461e1600cd686a2cf5e2dc6f931c7132853f7636
diff --git a/src/com/android/contacts/activities/ContactListActivity.java b/src/com/android/contacts/activities/ContactListActivity.java
index ed098a7..98fc88c 100644
--- a/src/com/android/contacts/activities/ContactListActivity.java
+++ b/src/com/android/contacts/activities/ContactListActivity.java
@@ -245,7 +245,6 @@
             }
             case NavigationBar.MODE_SEARCH: {
                 mListFragment = createContactSearchFragment();
-                mListFragment.setQueryString(mNavigationBar.getQueryString());
                 break;
             }
         }
@@ -255,6 +254,12 @@
             mListFragment.restoreSavedState(savedState);
         }
 
+        if (mode == NavigationBar.MODE_SEARCH) {
+            mListFragment.setQueryString(mNavigationBar.getQueryString());
+        }
+
+        setupContactDetailFragment(mListFragment.getSelectedContactUri());
+
         openFragmentTransaction()
                 .replace(R.id.two_pane_list, mListFragment)
                 .commit();
@@ -428,6 +433,7 @@
     private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
         public void onViewContactAction(Uri contactLookupUri) {
             if (mTwoPaneLayout) {
+                mListFragment.setSelectedContactUri(contactLookupUri);
                 setupContactDetailFragment(contactLookupUri);
             } else {
                 startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index cfe0830..1e6f18e 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -17,7 +17,12 @@
 
 import com.android.contacts.R;
 
+import android.content.CursorLoader;
+import android.content.Loader;
+import android.database.Cursor;
 import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
 import android.widget.ListView;
 
 /**
@@ -27,9 +32,68 @@
 public abstract class ContactBrowseListFragment extends
         ContactEntryListFragment<ContactListAdapter> {
 
+    private static final String KEY_SELECTED_URI = "selectedUri";
+
+    private static final int SELECTED_ID_LOADER = -3;
+
+    private Uri mSelectedContactUri;
+
     private OnContactBrowserActionListener mListener;
 
     @Override
+    public void restoreSavedState(Bundle savedState) {
+        super.restoreSavedState(savedState);
+
+        if (savedState == null) {
+            return;
+        }
+
+        mSelectedContactUri = savedState.getParcelable(KEY_SELECTED_URI);
+    }
+
+    @Override
+    public void onSaveInstanceState(Bundle outState) {
+        super.onSaveInstanceState(outState);
+        outState.putParcelable(KEY_SELECTED_URI, mSelectedContactUri);
+    }
+
+    @Override
+    public void onStart() {
+        super.onStart();
+        if (mSelectedContactUri != null && isSelectionVisible()) {
+            startLoading(SELECTED_ID_LOADER, null);
+        }
+   }
+
+    @Override
+    protected Loader<Cursor> onCreateLoader(int id, Bundle args) {
+        if (id == SELECTED_ID_LOADER) {
+            return new CursorLoader(getContext(),
+                    mSelectedContactUri,
+                    new String[] { Contacts._ID },
+                    null,
+                    null,
+                    null);
+        }
+
+        return super.onCreateLoader(id, args);
+    }
+
+    @Override
+    protected void onLoadFinished(Loader<Cursor> loader, Cursor data) {
+        if (loader.getId() == SELECTED_ID_LOADER) {
+            long selectedId = ListView.INVALID_ROW_ID;
+            if (data.moveToFirst()) {
+                selectedId = data.getLong(0);
+            }
+            getAdapter().setSelectedContactId(selectedId);
+            return;
+        }
+
+        super.onLoadFinished(loader, data);
+    }
+
+    @Override
     protected void prepareEmptyView() {
         if (isSearchMode()) {
             return;
@@ -48,6 +112,26 @@
         }
     }
 
+    public Uri getSelectedContactUri() {
+        return mSelectedContactUri;
+    }
+
+    public void setSelectedContactUri(Uri uri) {
+        if ((mSelectedContactUri == null && uri != null)
+                || (mSelectedContactUri != null && !mSelectedContactUri.equals(uri))) {
+            this.mSelectedContactUri = uri;
+            if (mSelectedContactUri != null) {
+                CursorLoader loader = (CursorLoader)getLoader(SELECTED_ID_LOADER);
+                if (loader == null) {
+                    startLoading(SELECTED_ID_LOADER, null);
+                } else {
+                    loader.setUri(mSelectedContactUri);
+                    loader.forceLoad();
+                }
+            }
+        }
+    }
+
     public void setOnContactListActionListener(OnContactBrowserActionListener listener) {
         mListener = listener;
     }
@@ -89,18 +173,4 @@
         super.finish();
         mListener.onFinishAction();
     }
-
-    @Override
-    protected void completeRestoreInstanceState() {
-        super.completeRestoreInstanceState();
-        ListView listView = getListView();
-        if (listView.getChoiceMode() == ListView.CHOICE_MODE_SINGLE) {
-            Uri checkedUri = null;
-            int position = listView.getCheckedItemPosition();
-            if (position != -1) {
-                checkedUri = getAdapter().getContactUri(position - listView.getHeaderViewsCount());
-            }
-            mListener.onViewContactAction(checkedUri);
-        }
-    }
 }
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index dc75659..f71fd09 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -61,6 +61,8 @@
     private boolean mLoading = true;
     private boolean mEmptyListEnabled = true;
 
+    private boolean mSelectionVisible;
+
     public ContactEntryListAdapter(Context context) {
         super(context, R.layout.list_section, R.id.header_text);
         addPartitions();
@@ -195,6 +197,14 @@
         mEmptyListEnabled = flag;
     }
 
+    public boolean isSelectionVisible() {
+        return mSelectionVisible;
+    }
+
+    public void setSelectionVisible(boolean flag) {
+        this.mSelectionVisible = flag;
+    }
+
     public void configureDirectoryLoader(DirectoryListLoader loader) {
         loader.setDirectorySearchEnabled(mDirectorySearchEnabled);
     }
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 1cb8969..8c491a2 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -448,10 +448,6 @@
 
     public void setSelectionVisible(boolean flag) {
         this.mSelectionVisible = flag;
-        if (mListView != null) {
-            mListView.setChoiceMode(
-                    mSelectionVisible ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE);
-        }
     }
 
     public void setSearchMode(boolean flag) {
@@ -591,9 +587,6 @@
         // We manually save/restore the listview state
         mListView.setSaveEnabled(false);
 
-        mListView.setChoiceMode(
-                mSelectionVisible ? ListView.CHOICE_MODE_SINGLE : ListView.CHOICE_MODE_NONE);
-
         if (mContextMenuAdapter != null) {
             mListView.setOnCreateContextMenuListener(mContextMenuAdapter);
         }
@@ -631,6 +624,7 @@
         mAdapter.setSortOrder(mSortOrder);
         mAdapter.setNameHighlightingEnabled(isNameHighlighingEnabled());
         mAdapter.setSectionHeaderDisplayEnabled(mSectionHeaderDisplayEnabled);
+        mAdapter.setSelectionVisible(mSelectionVisible);
     }
 
     protected boolean isNameHighlighingEnabled() {
diff --git a/src/com/android/contacts/list/ContactListAdapter.java b/src/com/android/contacts/list/ContactListAdapter.java
index 4c602c8..fd949ec 100644
--- a/src/com/android/contacts/list/ContactListAdapter.java
+++ b/src/com/android/contacts/list/ContactListAdapter.java
@@ -80,6 +80,8 @@
     private int mDisplayNameColumnIndex;
     private int mAlternativeDisplayNameColumnIndex;
 
+    private long mSelectedContactId;
+
     public ContactListAdapter(Context context) {
         super(context);
 
@@ -90,6 +92,17 @@
         return mUnknownNameText;
     }
 
+    public long getSelectedContactId() {
+        return mSelectedContactId;
+    }
+
+    public void setSelectedContactId(long selectedId) {
+        if (mSelectedContactId != selectedId) {
+            this.mSelectedContactId = selectedId;
+            notifyDataSetChanged();
+        }
+    }
+
     protected static Uri buildSectionIndexerUri(Uri uri) {
         return uri.buildUpon()
                 .appendQueryParameter(ContactCounts.ADDRESS_BOOK_INDEX_EXTRAS, "true").build();
diff --git a/src/com/android/contacts/list/ContactListItemView.java b/src/com/android/contacts/list/ContactListItemView.java
index 2606425..102497a 100644
--- a/src/com/android/contacts/list/ContactListItemView.java
+++ b/src/com/android/contacts/list/ContactListItemView.java
@@ -29,33 +29,32 @@
 import android.graphics.Canvas;
 import android.graphics.Typeface;
 import android.graphics.drawable.Drawable;
-import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.CommonDataKinds.Email;
 import android.provider.ContactsContract.CommonDataKinds.Nickname;
 import android.provider.ContactsContract.CommonDataKinds.Organization;
+import android.provider.ContactsContract.Contacts;
 import android.text.TextUtils;
 import android.text.TextUtils.TruncateAt;
 import android.util.AttributeSet;
 import android.view.Gravity;
 import android.view.View;
 import android.view.ViewGroup;
-import android.widget.Checkable;
 import android.widget.ImageView;
+import android.widget.ImageView.ScaleType;
 import android.widget.QuickContactBadge;
 import android.widget.TextView;
-import android.widget.ImageView.ScaleType;
 
 /**
  * A custom view for an item in the contact list.
  */
-public class ContactListItemView extends ViewGroup implements Checkable {
+public class ContactListItemView extends ViewGroup {
 
     private static final int QUICK_CONTACT_BADGE_STYLE =
             com.android.internal.R.attr.quickContactBadgeStyleWindowMedium;
 
     protected final Context mContext;
 
-    private boolean mChecked;
+    private boolean mItemSelected;
 
     private final int mPreferredHeight;
     private final int mVerticalDividerMargin;
@@ -69,7 +68,7 @@
     private final int mPresenceIconMargin;
     private final int mHeaderTextWidth;
 
-    private Drawable mCheckedBackgroundDrawable;
+    private Drawable mSelectedBackgroundDrawable;
 
     private boolean mHorizontalDividerVisible = true;
     private Drawable mHorizontalDividerDrawable;
@@ -267,9 +266,9 @@
             topBound += mHeaderBackgroundHeight;
         }
 
-        if (mChecked) {
+        if (mItemSelected) {
             ensureCheckedBackgroundDivider();
-            mCheckedBackgroundDrawable.setBounds(0, topBound, width, height);
+            mSelectedBackgroundDrawable.setBounds(0, topBound, width, height);
         }
 
         // Positions of views on the left are fixed and so are those on the right side.
@@ -405,10 +404,10 @@
      * Loads the drawable for the item background used when the item is checked.
      */
     private void ensureCheckedBackgroundDivider() {
-        if (mCheckedBackgroundDrawable == null) {
-            mCheckedBackgroundDrawable = mContext.getResources().getDrawable(
+        if (mSelectedBackgroundDrawable == null) {
+            mSelectedBackgroundDrawable = mContext.getResources().getDrawable(
                     R.drawable.list_item_checked_bg);
-            mCheckedBackgroundDrawable.setBounds(0, 0, getWidth(), getHeight());
+            mSelectedBackgroundDrawable.setBounds(0, 0, getWidth(), getHeight());
         }
     }
 
@@ -465,8 +464,8 @@
 
     @Override
     public void dispatchDraw(Canvas canvas) {
-        if (mChecked) {
-            mCheckedBackgroundDrawable.draw(canvas);
+        if (mItemSelected) {
+            mSelectedBackgroundDrawable.draw(canvas);
         }
         if (mHeaderVisible) {
             mHeaderBackgroundDrawable.draw(canvas);
@@ -844,21 +843,14 @@
         setData(dataBuffer.data, dataBuffer.sizeCopied);
     }
 
-    @Override
-    public boolean isChecked() {
-        return mChecked;
+    public boolean isItemSelected() {
+        return mItemSelected;
     }
 
-    @Override
-    public void setChecked(boolean checked) {
-        if (mChecked != checked) {
-            mChecked = checked;
+    public void setItemSelected(boolean selected) {
+        if (mItemSelected != selected) {
+            mItemSelected = selected;
             requestLayout();
         }
     }
-
-    @Override
-    public void toggle() {
-        setChecked(!mChecked);
-    }
 }
diff --git a/src/com/android/contacts/list/DefaultContactListAdapter.java b/src/com/android/contacts/list/DefaultContactListAdapter.java
index 14c09e6..c1bb1e8 100644
--- a/src/com/android/contacts/list/DefaultContactListAdapter.java
+++ b/src/com/android/contacts/list/DefaultContactListAdapter.java
@@ -98,6 +98,10 @@
     protected void bindView(View itemView, int partition, Cursor cursor, int position) {
         final ContactListItemView view = (ContactListItemView)itemView;
 
+        if (isSelectionVisible()) {
+            view.setItemSelected(getSelectedContactId() == cursor.getLong(CONTACT_ID_COLUMN_INDEX));
+        }
+
         bindSectionHeaderAndDivider(view, position);
 
         if (isQuickContactEnabled()) {
diff --git a/src/com/android/contacts/list/StrequentContactListAdapter.java b/src/com/android/contacts/list/StrequentContactListAdapter.java
index b5aec28..c198c38 100644
--- a/src/com/android/contacts/list/StrequentContactListAdapter.java
+++ b/src/com/android/contacts/list/StrequentContactListAdapter.java
@@ -198,6 +198,10 @@
     protected void bindView(View itemView, int partition, Cursor cursor, int position) {
         final ContactListItemView view = (ContactListItemView)itemView;
 
+        if (isSelectionVisible()) {
+            view.setItemSelected(getSelectedContactId() == cursor.getLong(CONTACT_ID_COLUMN_INDEX));
+        }
+
         bindName(view, cursor);
         bindQuickContact(view, cursor);
         bindPresence(view, cursor);