Merge "Moving state machine logic from activity into fragment."
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index c72f0d8..4d56be3 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -54,12 +54,8 @@
 import android.content.ActivityNotFoundException;
 import android.content.ContentValues;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.net.Uri;
 import android.os.Bundle;
-import android.os.Handler;
-import android.os.Message;
-import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents;
@@ -99,23 +95,6 @@
 
     private static final int DEFAULT_DIRECTORY_RESULT_LIMIT = 20;
 
-    /**
-     * The id for a delayed message that triggers automatic selection of the first
-     * found contact in search mode.
-     */
-    private static final int MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT = 1;
-
-    /**
-     * The delay that is used for automatically selecting the first found contact.
-     */
-    private static final int DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS = 500;
-
-    /**
-     * The minimum number of characters in the search query that is required
-     * before we automatically select the first found contact.
-     */
-    private static final int AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH = 2;
-
     private static final String KEY_SEARCH_MODE = "searchMode";
 
     private DialogManager mDialogManager = new DialogManager(this);
@@ -123,8 +102,6 @@
     private ContactsIntentResolver mIntentResolver;
     private ContactsRequest mRequest;
 
-    private SharedPreferences mPrefs;
-
     private boolean mHasActionBar;
     private ActionBarAdapter mActionBarAdapter;
 
@@ -146,8 +123,6 @@
 
     private ImageButton mAddContactImageView;
 
-    private Handler mHandler;
-
     private ContactsUnavailableFragment mContactsUnavailableFragment;
     private ProviderStatusLoader mProviderStatusLoader;
     private int mProviderStatus = -1;
@@ -159,20 +134,6 @@
         mProviderStatusLoader = new ProviderStatusLoader(this);
     }
 
-    private Handler getHandler() {
-        if (mHandler == null) {
-            mHandler = new Handler() {
-                @Override
-                public void handleMessage(Message msg) {
-                    if (msg.what == MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT) {
-                        selectFirstFoundContact();
-                    }
-                }
-            };
-        }
-        return mHandler;
-    }
-
     public boolean areContactsAvailable() {
         return mProviderStatus == ProviderStatus.STATUS_NORMAL;
     }
@@ -182,7 +143,6 @@
         if (fragment instanceof ContactBrowseListFragment) {
             mListFragment = (ContactBrowseListFragment)fragment;
             mListFragment.setOnContactListActionListener(new ContactBrowserActionListener());
-            configureListSelection();
         } else if (fragment instanceof ContactDetailFragment) {
             mDetailFragment = (ContactDetailFragment)fragment;
             mDetailFragment.setListener(mDetailFragmentListener);
@@ -218,8 +178,6 @@
             return;
         }
 
-        mPrefs = PreferenceManager.getDefaultSharedPreferences(this);
-
         setTitle(mRequest.getActivityTitle());
         setContentView(R.layout.contact_browser);
 
@@ -268,18 +226,10 @@
 
             if (mHasActionBar) {
                 mActionBarAdapter.setSearchMode(false);
-
-                // onNewIntent is called when the activity is paused, so it is not
-                // registered as a listener of the action bar adapter. Simulate the listener call.
-                onAction();
+                configureFragments(false /* from request */);
             }
 
             mListFragment.setSelectedContactUri(uri);
-            mListFragment.saveSelectedUri(mPrefs);
-            mListFragment.requestSelectionOnScreen(true);
-            if (mContactContentDisplayed) {
-                setupContactDetailFragment(uri);
-            }
         }
     }
 
@@ -338,6 +288,11 @@
                 mContactListFilterController.setContactListFilter(new ContactListFilter(
                         ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS), false);
             }
+
+            if (mRequest.getContactUri() != null) {
+                searchMode = false;
+            }
+
         } else if (mHasActionBar) {
             searchMode = mActionBarAdapter.isSearchMode();
         }
@@ -352,19 +307,17 @@
 
             if (mSearchMode) {
                 mListFragment = createContactSearchFragment();
-                // When switching to the search mode, erase previous state of the search UI
-                mListFragment.eraseSelectedUri(mPrefs);
             } else {
                 mListFragment = createListFragment(ContactsRequest.ACTION_DEFAULT);
+                if (mRequest.getContactUri() != null) {
+                    mListFragment.setSelectedContactUri(mRequest.getContactUri());
+
+                }
             }
         }
 
-        if (mSearchMode) {
-            if (mHasActionBar) {
-                mListFragment.setQueryString(mActionBarAdapter.getQueryString());
-            }
-        } else {
-            configureListSelection();
+        if (mSearchMode && mHasActionBar) {
+            mListFragment.setQueryString(mActionBarAdapter.getQueryString());
         }
 
         if (replaceList) {
@@ -372,155 +325,40 @@
                     .replace(R.id.list_container, mListFragment)
                     .commit();
         }
+
+        if (mContactContentDisplayed && mDetailFragment == null) {
+            mDetailFragment = new ContactDetailFragment();
+            getFragmentManager().openTransaction()
+                    .replace(R.id.detail_container, mDetailFragment)
+                    .commit();
+        }
     }
 
     @Override
     public void onContactListFiltersLoaded() {
-        configureListSelection();
+        if (mListFragment == null) {
+            return;
+        }
+
+        mListFragment.setFilter(mContactListFilterController.getFilter());
 
         // Filters have been loaded - now we can start loading the list itself
         mListFragment.startLoading();
+
+        invalidateOptionsMenu();
     }
 
     @Override
     public void onContactListFilterChanged() {
-        resetContactSelectionInIntent();
-
         if (mListFragment == null) {
             return;
         }
 
-        DefaultContactBrowseListFragment fragment =
-                (DefaultContactBrowseListFragment) mListFragment;
-        ContactListFilter filter = mContactListFilterController.getFilter();
-        fragment.setFilter(filter);
-        fragment.reloadData();
-        fragment.restoreSelectedUri(mPrefs);
-        fragment.requestSelectionOnScreen(false);
-
-        if (mContactContentDisplayed) {
-            setupContactDetailFragment(mListFragment.getSelectedContactUri());
-        }
+        mListFragment.setFilter(mContactListFilterController.getFilter());
 
         invalidateOptionsMenu();
     }
 
-    /**
-     * Configures filter-specific persistent selection.
-     */
-    private void configureListSelection() {
-        if (mListFragment == null) {
-            return;
-        }
-
-        if (mListFragment instanceof DefaultContactBrowseListFragment
-                && mContactListFilterController != null
-                && mContactListFilterController.isLoaded()) {
-            DefaultContactBrowseListFragment fragment =
-                    (DefaultContactBrowseListFragment) mListFragment;
-            ContactListFilter filter = mContactListFilterController.getFilter();
-            fragment.setFilter(filter);
-            if (mRequest.getContactUri() != null) {
-                fragment.setSelectedContactUri(mRequest.getContactUri());
-                fragment.saveSelectedUri(mPrefs);
-            } else {
-                fragment.restoreSelectedUri(mPrefs);
-            }
-            fragment.requestSelectionOnScreen(false);
-            if (mContactContentDisplayed) {
-                setupContactDetailFragment(mListFragment.getSelectedContactUri());
-            }
-        } else if (mContactContentDisplayed) {
-            setupContactDetailFragment(mListFragment.getSelectedContactUri());
-        }
-
-        invalidateOptionsMenu();
-    }
-
-    /**
-     * Removes the selected contact URI that was supplied with the intent (if any),
-     * because the user has explicitly changed the selection.
-     */
-    private void resetContactSelectionInIntent() {
-        mRequest.setContactUri(null);
-
-        getIntent().setAction(Intent.ACTION_DEFAULT);
-        getIntent().setData(null);
-    }
-
-    private void showDefaultSelection() {
-        if (mSearchMode) {
-            selectFirstFoundContactAfterDelay();
-            return;
-        }
-
-        Uri requestedContactUri = mRequest.getContactUri();
-        if (requestedContactUri != null
-                && mListFragment instanceof DefaultContactBrowseListFragment) {
-            // If a specific selection was requested, adjust the filter so
-            // that the requested selection is unconditionally visible.
-            DefaultContactBrowseListFragment fragment =
-                    (DefaultContactBrowseListFragment) mListFragment;
-            ContactListFilter filter =
-                    new ContactListFilter(ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
-            fragment.setFilter(filter);
-            fragment.setSelectedContactUri(requestedContactUri);
-            fragment.saveSelectedUri(mPrefs);
-            fragment.reloadData();
-            if (mContactListFilterController != null) {
-                mContactListFilterController.setContactListFilter(filter, true);
-            }
-        } else {
-            // Otherwise, choose the first contact on the list and select it
-            requestedContactUri = mListFragment.getFirstContactUri();
-            if (requestedContactUri != null) {
-                mListFragment.setSelectedContactUri(requestedContactUri);
-                mListFragment.eraseSelectedUri(mPrefs);
-                mListFragment.requestSelectionOnScreen(false);
-            }
-        }
-        if (mContactContentDisplayed) {
-            setupContactDetailFragment(requestedContactUri);
-        }
-
-        invalidateOptionsMenu();
-    }
-
-    /**
-     * Automatically selects the first found contact in search mode.  The selection
-     * is updated after a delay to allow the user to type without to much UI churn
-     * and to save bandwidth on directory queries.
-     */
-    public void selectFirstFoundContactAfterDelay() {
-        Handler handler = getHandler();
-        handler.removeMessages(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT);
-        handler.sendEmptyMessageDelayed(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT,
-                DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS);
-    }
-
-    /**
-     * Selects the first contact in the list in search mode.
-     */
-    protected void selectFirstFoundContact() {
-        if (!mSearchMode) {
-            return;
-        }
-
-        Uri selectedUri = null;
-        String queryString = mListFragment.getQueryString();
-        if (queryString != null
-                && queryString.length() >= AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH) {
-            selectedUri = mListFragment.getFirstContactUri();
-        }
-
-        mListFragment.setSelectedContactUri(selectedUri);
-        mListFragment.eraseSelectedUri(mPrefs);
-        mListFragment.requestSelectionOnScreen(true);
-        if (mContactContentDisplayed) {
-            setupContactDetailFragment(selectedUri);
-        }
-    }
-
     @Override
     public void onContactListFilterCustomizationRequest() {
         startActivityForResult(new Intent(this, CustomContactListFilterActivity.class),
@@ -528,15 +366,7 @@
     }
 
     private void setupContactDetailFragment(final Uri contactLookupUri) {
-        if (mDetailFragment == null) {
-            mDetailFragment = new ContactDetailFragment();
-            mDetailFragment.loadUri(contactLookupUri);
-            getFragmentManager().openTransaction()
-                    .replace(R.id.detail_container, mDetailFragment)
-                    .commit();
-        } else {
-            mDetailFragment.loadUri(contactLookupUri);
-        }
+        mDetailFragment.loadUri(contactLookupUri);
     }
 
     /**
@@ -571,6 +401,10 @@
                 fragment.setSelectionVisible(mContactContentDisplayed);
                 fragment.setQuickContactEnabled(!mContactContentDisplayed);
                 fragment.setFilterEnabled(!mRequest.isSearchMode());
+                fragment.setPersistentSelectionEnabled(!mRequest.isSearchMode());
+                if (mContactListFilterController.isLoaded()) {
+                    fragment.setFilter(mContactListFilterController.getFilter());
+                }
                 return fragment;
             }
 
@@ -680,13 +514,17 @@
     }
 
     private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
+
+        @Override
+        public void onSelectionChange() {
+            if (mContactContentDisplayed) {
+                setupContactDetailFragment(mListFragment.getSelectedContactUri());
+            }
+        }
+
         @Override
         public void onViewContactAction(Uri contactLookupUri) {
             if (mContactContentDisplayed) {
-                resetContactSelectionInIntent();
-
-                mListFragment.setSelectedContactUri(contactLookupUri);
-                mListFragment.saveSelectedUri(mPrefs);
                 setupContactDetailFragment(contactLookupUri);
             } else {
                 startActivity(new Intent(Intent.ACTION_VIEW, contactLookupUri));
@@ -749,16 +587,17 @@
 
         @Override
         public void onInvalidSelection() {
-            showDefaultSelection();
+            ContactListFilter filter =
+                    new ContactListFilter(ContactListFilter.FILTER_TYPE_SINGLE_CONTACT);
+            mContactListFilterController.setContactListFilter(filter, true);
+            mListFragment.setFilter(filter);
         }
     }
 
     private class DetailFragmentListener implements ContactDetailFragment.Listener {
         @Override
         public void onContactNotFound() {
-            resetContactSelectionInIntent();
-            setupContactDetailFragment(null);
-            showDefaultSelection();
+            // Nothing needs to be done here
         }
 
         @Override
@@ -891,9 +730,8 @@
         }
 
         boolean groupActionsEnabled = false;
-        if (mListFragment instanceof DefaultContactBrowseListFragment) {
-            ContactListFilter filter =
-                    ((DefaultContactBrowseListFragment)mListFragment).getFilter();
+        if (mListFragment != null) {
+            ContactListFilter filter = mListFragment.getFilter();
             if (filter != null
                     && filter.filterType == ContactListFilter.FILTER_TYPE_GROUP
                     && !filter.groupReadOnly) {
@@ -944,15 +782,13 @@
                 return true;
             }
             case R.id.menu_rename_group: {
-                ContactListFilter filter =
-                        ((DefaultContactBrowseListFragment)mListFragment).getFilter();
+                ContactListFilter filter = mListFragment.getFilter();
                 GroupRenamingDialogFragment.show(getFragmentManager(), filter.groupId,
                         filter.title);
                 return true;
             }
             case R.id.menu_delete_group: {
-                ContactListFilter filter =
-                        ((DefaultContactBrowseListFragment)mListFragment).getFilter();
+                ContactListFilter filter = mListFragment.getFilter();
                 GroupDeletionDialogFragment.show(getFragmentManager(), filter.groupId,
                         filter.title);
                 return true;
@@ -1045,24 +881,12 @@
                 }
                 break;
             }
-            case SUBACTIVITY_EDIT_CONTACT: {
-                mListFragment.requestSelectionOnScreen(true);
-                break;
-            }
 
+            case SUBACTIVITY_EDIT_CONTACT:
             case SUBACTIVITY_NEW_CONTACT: {
-                if (resultCode == RESULT_OK && mContactContentDisplayed) {
-                    resetContactSelectionInIntent();
-
-                    final Uri newContactUri = data.getData();
-                    if (mContactContentDisplayed) {
-                        setupContactDetailFragment(newContactUri);
-                    }
-
+                if (resultCode == RESULT_OK) {
                     mRequest.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
-                    mListFragment.setSelectedContactUri(newContactUri);
-                    mListFragment.saveSelectedUri(mPrefs);
-                    mListFragment.requestSelectionOnScreen(true);
+                    mListFragment.setSelectedContactUri(data.getData());
                 }
                 break;
             }
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index 197dc51..a1c7c3f 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -18,13 +18,18 @@
 import com.android.contacts.R;
 import com.android.contacts.widget.ListViewUtils;
 
+import android.app.Activity;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.CursorLoader;
 import android.content.Loader;
 import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
 import android.database.Cursor;
 import android.net.Uri;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Directory;
@@ -40,19 +45,50 @@
 
     private static final String KEY_SELECTED_URI = "selectedUri";
     private static final String KEY_SELECTION_VERIFIED = "selectionVerified";
+    private static final String KEY_FILTER_ENABLED = "filterEnabled";
+
+    private static final String KEY_PERSISTENT_SELECTION_ENABLED = "persistenSelectionEnabled";
+    private static final String PERSISTENT_SELECTION_PREFIX = "defaultContactBrowserSelection";
+
+    /**
+     * The id for a delayed message that triggers automatic selection of the first
+     * found contact in search mode.
+     */
+    private static final int MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT = 1;
+
+    /**
+     * The delay that is used for automatically selecting the first found contact.
+     */
+    private static final int DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS = 500;
+
+    /**
+     * The minimum number of characters in the search query that is required
+     * before we automatically select the first found contact.
+     */
+    private static final int AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH = 2;
+
+    private static final int MESSAGE_REQUEST_SELECTION_TO_SCREEN = 2;
 
     private static final int SELECTED_ID_LOADER = -3;
+    private static final String ARG_CONTACT_URI = "uri";
 
-    private static final int SELECTION_VISIBILITY_REQUEST_NONE = 0;
-    private static final int SELECTION_VISIBILITY_REQUEST_SMOOTH = 1;
-    private static final int SELECTION_VISIBILITY_REQUEST_INSTANT = 2;
+    private SharedPreferences mPrefs;
+    private Handler mHandler;
 
+    private boolean mStartedLoading;
+    private boolean mSelectionRequired;
+    private boolean mSelectionToScreenRequested;
+    private boolean mSmoothScrollRequested;
+    private boolean mSelectionPersistenceRequested;
     private Uri mSelectedContactUri;
     private long mSelectedContactDirectoryId;
     private String mSelectedContactLookupKey;
-    private int mSelectionVisibilityRequest;
     private boolean mSelectionVerified;
     private boolean mLoadingLookupKey;
+    private boolean mFilterEnabled;
+    private ContactListFilter mFilter;
+    private boolean mPersistentSelectionEnabled;
+    private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
 
     protected OnContactBrowserActionListener mListener;
 
@@ -60,30 +96,79 @@
 
         @Override
         public Loader<Cursor> onCreateLoader(int id, Bundle args) {
-            return new CursorLoader(getContext(),
-                    mSelectedContactUri,
-                    new String[] { Contacts.LOOKUP_KEY },
-                    null,
-                    null,
-                    null);
+            Uri contactUri = args.getParcelable(ARG_CONTACT_URI);
+            return new CursorLoader(getContext(), contactUri, new String[] { Contacts.LOOKUP_KEY },
+                    null, null, null);
         }
 
         @Override
         public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-            mLoadingLookupKey = false;
             String lookupKey = null;
             if (data != null) {
                 if (data.moveToFirst()) {
                     lookupKey = data.getString(0);
                 }
             }
-            if (!TextUtils.equals(mSelectedContactLookupKey, lookupKey)) {
-                mSelectedContactLookupKey = lookupKey;
-                configureContactSelection();
-            }
+            onLookupKeyLoadFinished(lookupKey);
         }
     };
 
+    private Handler getHandler() {
+        if (mHandler == null) {
+            mHandler = new Handler() {
+                @Override
+                public void handleMessage(Message msg) {
+                    switch (msg.what) {
+                        case MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT:
+                            selectDefaultContact();
+                            break;
+                        case MESSAGE_REQUEST_SELECTION_TO_SCREEN:
+                            requestSelectionToScreen();
+                            break;
+                    }
+                }
+            };
+        }
+        return mHandler;
+    }
+
+    @Override
+    public void onAttach(Activity activity) {
+        super.onAttach(activity);
+        mPrefs = PreferenceManager.getDefaultSharedPreferences(activity);
+        restoreSelectedUri();
+    }
+
+    public void setPersistentSelectionEnabled(boolean flag) {
+        this.mPersistentSelectionEnabled = flag;
+    }
+
+    public void setFilter(ContactListFilter filter) {
+        if (mFilter == null && filter == null) {
+            return;
+        }
+
+        if (mFilter != null && mFilter.equals(filter)) {
+            return;
+        }
+
+        mFilter = filter;
+        restoreSelectedUri();
+        reloadData();
+    }
+
+    public ContactListFilter getFilter() {
+        return mFilter;
+    }
+
+    public boolean isFilterEnabled() {
+        return mFilterEnabled;
+    }
+
+    public void setFilterEnabled(boolean flag) {
+        this.mFilterEnabled = flag;
+    }
+
     @Override
     public void restoreSavedState(Bundle savedState) {
         super.restoreSavedState(savedState);
@@ -92,6 +177,8 @@
             return;
         }
 
+        mPersistentSelectionEnabled = savedState.getBoolean(KEY_PERSISTENT_SELECTION_ENABLED);
+        mFilterEnabled = savedState.getBoolean(KEY_FILTER_ENABLED);
         mSelectedContactUri = savedState.getParcelable(KEY_SELECTED_URI);
         mSelectionVerified = savedState.getBoolean(KEY_SELECTION_VERIFIED);
         parseSelectedContactUri();
@@ -100,6 +187,7 @@
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
+        outState.putBoolean(KEY_FILTER_ENABLED, mFilterEnabled);
         outState.putParcelable(KEY_SELECTED_URI, mSelectedContactUri);
         outState.putBoolean(KEY_SELECTION_VERIFIED, mSelectionVerified);
     }
@@ -112,14 +200,36 @@
    }
 
     protected void startLoadingContactLookupKey() {
-        if (isSelectionVisible() && mSelectedContactUri != null &&
-                (mSelectedContactDirectoryId == Directory.DEFAULT ||
-                        mSelectedContactDirectoryId == Directory.LOCAL_INVISIBLE)) {
-            mLoadingLookupKey = true;
-            getLoaderManager().restartLoader(SELECTED_ID_LOADER, null, mIdLoaderCallbacks);
-        } else {
-            getLoaderManager().stopLoader(SELECTED_ID_LOADER);
+        getLoaderManager().stopLoader(SELECTED_ID_LOADER);
+
+        if (!isSelectionVisible()) {
+            return;
         }
+
+        mLoadingLookupKey = true;
+
+        if (mSelectedContactUri == null) {
+            onLookupKeyLoadFinished(null);
+            return;
+        }
+
+        if (mSelectedContactDirectoryId != Directory.DEFAULT
+                && mSelectedContactDirectoryId != Directory.LOCAL_INVISIBLE) {
+            onLookupKeyLoadFinished(mSelectedContactLookupKey);
+        } else {
+            Bundle bundle = new Bundle();
+            bundle.putParcelable(ARG_CONTACT_URI, mSelectedContactUri);
+            getLoaderManager().restartLoader(SELECTED_ID_LOADER, bundle, mIdLoaderCallbacks);
+      }
+    }
+
+    protected void onLookupKeyLoadFinished(String lookupKey) {
+        mLoadingLookupKey = false;
+        if (!TextUtils.equals(mSelectedContactLookupKey, lookupKey)) {
+            mSelectedContactLookupKey = lookupKey;
+            getAdapter().setSelectedContact(mSelectedContactDirectoryId, mSelectedContactLookupKey);
+        }
+        checkSelection();
     }
 
     @Override
@@ -145,17 +255,31 @@
         return mSelectedContactUri;
     }
 
+    /**
+     * Sets the new selection for the list.
+     */
     public void setSelectedContactUri(Uri uri) {
+        setSelectedContactUri(uri, true, true, true);
+    }
+
+    private void setSelectedContactUri(
+            Uri uri, boolean required, boolean smoothScroll, boolean persistent) {
+        mSmoothScrollRequested = smoothScroll;
+        mSelectionToScreenRequested = true;
+
         if ((mSelectedContactUri == null && uri != null)
                 || (mSelectedContactUri != null && !mSelectedContactUri.equals(uri))) {
+            mSelectionVerified = false;
+            mSelectionRequired = required;
+            mSelectionPersistenceRequested = persistent;
             mSelectedContactUri = uri;
-
             parseSelectedContactUri();
 
-            if (isAdded()) {
+            if (mStartedLoading) {
                 // Configure the adapter to show the selection based on the lookup key extracted
                 // from the URI
-                configureAdapter();
+                getAdapter().setSelectedContact(
+                        mSelectedContactDirectoryId, mSelectedContactLookupKey);
 
                 // Also, launch a loader to pick up a new lookup key in case it has changed
                 startLoadingContactLookupKey();
@@ -187,27 +311,24 @@
     @Override
     protected void configureAdapter() {
         super.configureAdapter();
-        configureContactSelection();
-    }
 
-    /**
-     * Configures the adapter with the identity of the currently selected contact.
-     */
-    private void configureContactSelection() {
         ContactListAdapter adapter = getAdapter();
         if (adapter == null) {
             return;
         }
 
+        if (mFilterEnabled && mFilter != null) {
+            adapter.setFilter(mFilter);
+        }
+
         adapter.setSelectedContact(mSelectedContactDirectoryId, mSelectedContactLookupKey);
-        checkSelection();
     }
 
     @Override
     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
         super.onLoadFinished(loader, data);
+        mSelectionVerified = false;
         checkSelection();
-        requestSelectionOnScreenIfNeeded();
     }
 
     private void checkSelection() {
@@ -220,10 +341,77 @@
         }
 
         ContactListAdapter adapter = getAdapter();
-        if (adapter.hasValidSelection()) {
-            mSelectionVerified = true;
-        } else {
+
+        int selectedPosition = adapter.getSelectedContactPosition();
+        if (mSelectionRequired && selectedPosition == -1) {
             notifyInvalidSelection();
+            return;
+        }
+
+        if (selectedPosition == -1) {
+            saveSelectedUri(null);
+
+            if (isSearchMode()) {
+                selectFirstFoundContactAfterDelay();
+            } else {
+                selectDefaultContact();
+            }
+
+            if (mSelectedContactUri != null) {
+                // The default selection is now loading and this method will be called again
+                return;
+            }
+        }
+
+        mSelectionRequired = false;
+        mSelectionVerified = true;
+
+        if (mSelectionPersistenceRequested) {
+            saveSelectedUri(mSelectedContactUri);
+            mSelectionPersistenceRequested = false;
+        }
+
+        if (mSelectionToScreenRequested) {
+            getHandler().removeMessages(MESSAGE_REQUEST_SELECTION_TO_SCREEN);
+            getHandler().sendEmptyMessage(MESSAGE_REQUEST_SELECTION_TO_SCREEN);
+        }
+
+        if (mListener != null) {
+            mListener.onSelectionChange();
+        }
+    }
+
+    /**
+     * Automatically selects the first found contact in search mode.  The selection
+     * is updated after a delay to allow the user to type without to much UI churn
+     * and to save bandwidth on directory queries.
+     */
+    public void selectFirstFoundContactAfterDelay() {
+        Handler handler = getHandler();
+        handler.removeMessages(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT);
+
+        String queryString = getQueryString();
+        if (queryString != null
+                && queryString.length() >= AUTOSELECT_FIRST_FOUND_CONTACT_MIN_QUERY_LENGTH) {
+            handler.sendEmptyMessageDelayed(MESSAGE_AUTOSELECT_FIRST_FOUND_CONTACT,
+                    DELAY_AUTOSELECT_FIRST_FOUND_CONTACT_MILLIS);
+        } else {
+            setSelectedContactUri(null, false, false, false);
+        }
+    }
+
+    protected void selectDefaultContact() {
+        Uri firstContactUri = getAdapter().getFirstContactUri();
+        setSelectedContactUri(firstContactUri, false, true, false);
+    }
+
+    protected void requestSelectionToScreen() {
+        int selectedPosition = getAdapter().getSelectedContactPosition();
+        if (selectedPosition != -1) {
+            ListView listView = getListView();
+            ListViewUtils.requestPositionToScreen(listView,
+                    selectedPosition + listView.getHeaderViewsCount(), mSmoothScrollRequested);
+            mSelectionToScreenRequested = false;
         }
     }
 
@@ -232,21 +420,21 @@
         return mLoadingLookupKey || super.isLoading();
     }
 
-    public Uri getFirstContactUri() {
-        ContactListAdapter adapter = getAdapter();
-        return adapter.getFirstContactUri();
-    }
-
     @Override
     public void startLoading() {
-        mSelectionVerified = false;
-        super.startLoading();
+        if (!mFilterEnabled || mFilter != null) {
+            mStartedLoading = true;
+            mSelectionVerified = false;
+            super.startLoading();
+        }
     }
 
     @Override
     public void reloadData() {
-        mSelectionVerified = false;
-        super.reloadData();
+        if (mStartedLoading) {
+            mSelectionVerified = false;
+            super.reloadData();
+        }
     }
 
     public void setOnContactListActionListener(OnContactBrowserActionListener listener) {
@@ -258,7 +446,8 @@
     }
 
     public void viewContact(Uri contactUri) {
-        if (mListener != null) mListener.onViewContactAction(contactUri);
+        setSelectedContactUri(contactUri, false, false, true);
+        if (mListener != null) { mListener.onViewContactAction(contactUri); }
     }
 
     public void editContact(Uri contactUri) {
@@ -295,45 +484,38 @@
         if (mListener != null) mListener.onFinishAction();
     }
 
-    public void requestSelectionOnScreen(boolean smooth) {
-        mSelectionVisibilityRequest = smooth
-                ? SELECTION_VISIBILITY_REQUEST_SMOOTH
-                : SELECTION_VISIBILITY_REQUEST_INSTANT;
-        requestSelectionOnScreenIfNeeded();
-    }
-
-    @Override
-    protected void completeRestoreInstanceState() {
-        super.completeRestoreInstanceState();
-        requestSelectionOnScreenIfNeeded();
-    }
-
-    private void requestSelectionOnScreenIfNeeded() {
-        if (mSelectionVisibilityRequest == SELECTION_VISIBILITY_REQUEST_NONE) {
+    private void saveSelectedUri(Uri contactUri) {
+        if (!mPersistentSelectionEnabled) {
             return;
         }
 
-        ContactListAdapter adapter = getAdapter();
-        if (adapter == null || adapter.isLoading()) {
+        Editor editor = mPrefs.edit();
+        if (contactUri == null) {
+            editor.remove(getPersistentSelectionKey());
+        } else {
+            editor.putString(getPersistentSelectionKey(), contactUri.toString());
+        }
+        editor.apply();
+    }
+
+    private void restoreSelectedUri() {
+        if (!mPersistentSelectionEnabled || mPrefs == null || mSelectionRequired) {
             return;
         }
 
-        int position = adapter.getSelectedContactPosition();
-        if (position != -1) {
-            boolean smooth = mSelectionVisibilityRequest == SELECTION_VISIBILITY_REQUEST_SMOOTH;
-            mSelectionVisibilityRequest = SELECTION_VISIBILITY_REQUEST_NONE;
-            ListView listView = getListView();
-            ListViewUtils.requestPositionToScreen(
-                    listView, position + listView.getHeaderViewsCount(), smooth);
+        String selectedUri = mPrefs.getString(getPersistentSelectionKey(), null);
+        if (selectedUri == null) {
+            setSelectedContactUri(null, false, false, false);
+        } else {
+            setSelectedContactUri(Uri.parse(selectedUri), false, false, false);
         }
     }
 
-    public void saveSelectedUri(SharedPreferences preferences) {
-    }
-
-    public void restoreSelectedUri(SharedPreferences preferences) {
-    }
-
-    public void eraseSelectedUri(SharedPreferences preferences) {
+    private String getPersistentSelectionKey() {
+        if (mFilter == null) {
+            return mPersistentSelectionPrefix;
+        } else {
+            return mPersistentSelectionPrefix + "-" + mFilter.getId();
+        }
     }
 }
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 7b5641b..f0b4a1d 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -872,10 +872,6 @@
         }
     }
 
-    private View findViewById(int id) {
-        return mView.findViewById(id);
-    }
-
     // TODO: fix PluralRules to handle zero correctly and use Resources.getQuantityText directly
     public String getQuantityText(int count, int zeroResourceId, int pluralResourceId) {
         if (count == 0) {
diff --git a/src/com/android/contacts/list/ContactsIntentResolver.java b/src/com/android/contacts/list/ContactsIntentResolver.java
index cc1cdb0..afe29df 100644
--- a/src/com/android/contacts/list/ContactsIntentResolver.java
+++ b/src/com/android/contacts/list/ContactsIntentResolver.java
@@ -141,6 +141,8 @@
         } else if (Intent.ACTION_VIEW.equals(action)) {
             request.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
             request.setContactUri(intent.getData());
+            intent.setAction(Intent.ACTION_DEFAULT);
+            intent.setData(null);
         } else if (UI.FILTER_CONTACTS_ACTION.equals(action)) {
             // When we get a FILTER_CONTACTS_ACTION, it represents search in the context
             // of some other action. Let's retrieve the original action to provide proper
@@ -157,10 +159,6 @@
                 }
             }
 
-            if (request == null) {
-                request = new ContactsRequest();
-            }
-
             request.setSearchMode(true);
 
         // Since this is the filter activity it receives all intents
@@ -176,6 +174,8 @@
             } else {
                 request.setActionCode(ContactsRequest.ACTION_VIEW_CONTACT);
                 request.setContactUri(data);
+                intent.setAction(Intent.ACTION_DEFAULT);
+                intent.setData(null);
             }
         } else if (Intents.SEARCH_SUGGESTION_DIAL_NUMBER_CLICKED.equals(action)) {
             request.setRedirectIntent(new Intent(Intent.ACTION_CALL_PRIVILEGED, intent.getData()));
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index e1100f6..3084a66 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -17,10 +17,7 @@
 
 import com.android.contacts.R;
 
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
 import android.database.Cursor;
-import android.net.Uri;
 import android.os.Bundle;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -36,36 +33,19 @@
  */
 public class DefaultContactBrowseListFragment extends ContactBrowseListFragment {
 
-    private static final String KEY_FILTER_ENABLED = "filterEnabled";
-
-    private static final String PERSISTENT_SELECTION_PREFIX = "defaultContactBrowserSelection";
-    private static final String KEY_SEARCH_MODE_CONTACT_URI_SUFFIX = "search";
-
     private View mCounterHeaderView;
     private View mSearchHeaderView;
 
-    private boolean mFilterEnabled;
-    private ContactListFilter mFilter;
-    private String mPersistentSelectionPrefix = PERSISTENT_SELECTION_PREFIX;
-
     public DefaultContactBrowseListFragment() {
         setPhotoLoaderEnabled(true);
         setSectionHeaderDisplayEnabled(true);
         setAizyEnabled(true);
     }
 
-    public void setFilter(ContactListFilter filter) {
-        mFilter = filter;
-    }
-
-    public ContactListFilter getFilter() {
-        return mFilter;
-    }
 
     @Override
     public void onSaveInstanceState(Bundle outState) {
         super.onSaveInstanceState(outState);
-        outState.putBoolean(KEY_FILTER_ENABLED, mFilterEnabled);
     }
 
     @Override
@@ -76,7 +56,6 @@
             return;
         }
 
-        setFilterEnabled(savedState.getBoolean(KEY_FILTER_ENABLED));
     }
 
     @Override
@@ -93,16 +72,6 @@
     }
 
     @Override
-    protected void configureAdapter() {
-        super.configureAdapter();
-
-        DefaultContactListAdapter adapter = (DefaultContactListAdapter)getAdapter();
-        if (adapter != null && mFilter != null) {
-            adapter.setFilter(mFilter);
-        }
-    }
-
-    @Override
     protected View inflateView(LayoutInflater inflater, ViewGroup container) {
         return inflater.inflate(R.layout.contacts_list_content, null);
     }
@@ -177,58 +146,4 @@
             }
         }
     }
-
-    public boolean isFilterEnabled() {
-        return mFilterEnabled;
-    }
-
-    public void setFilterEnabled(boolean flag) {
-        this.mFilterEnabled = flag;
-    }
-
-    @Override
-    public void startLoading() {
-        if (!mFilterEnabled || mFilter != null) {
-            super.startLoading();
-        }
-    }
-
-    @Override
-    public void saveSelectedUri(SharedPreferences preferences) {
-        Uri uri = getSelectedContactUri();
-        if (uri == null) {
-            eraseSelectedUri(preferences);
-        } else {
-            Editor editor = preferences.edit();
-            editor.putString(getPersistentSelectionKey(), uri.toString());
-            editor.apply();
-        }
-    }
-
-    @Override
-    public void eraseSelectedUri(SharedPreferences preferences) {
-        Editor editor = preferences.edit();
-        editor.remove(getPersistentSelectionKey());
-        editor.apply();
-    }
-
-    @Override
-    public void restoreSelectedUri(SharedPreferences preferences) {
-        String selectedUri = preferences.getString(getPersistentSelectionKey(), null);
-        if (selectedUri == null) {
-            setSelectedContactUri(null);
-        } else {
-            setSelectedContactUri(Uri.parse(selectedUri));
-        }
-    }
-
-    private String getPersistentSelectionKey() {
-        if (isSearchMode()) {
-            return mPersistentSelectionPrefix + "-" + KEY_SEARCH_MODE_CONTACT_URI_SUFFIX;
-        } else if (mFilter == null) {
-            return mPersistentSelectionPrefix;
-        } else {
-            return mPersistentSelectionPrefix + "-" + mFilter.getId();
-        }
-    }
 }
diff --git a/src/com/android/contacts/list/OnContactBrowserActionListener.java b/src/com/android/contacts/list/OnContactBrowserActionListener.java
index 80d4838..771f251 100644
--- a/src/com/android/contacts/list/OnContactBrowserActionListener.java
+++ b/src/com/android/contacts/list/OnContactBrowserActionListener.java
@@ -23,6 +23,12 @@
 public interface OnContactBrowserActionListener  {
 
     /**
+     * Notification of selection change, invoked when the selection of activated
+     * item(s) is change by either a user action or some other event, e.g. sync.
+     */
+    void onSelectionChange();
+
+    /**
      * Opens the specified contact for viewing.
      *
      * @param contactLookupUri The lookup-uri of the Contact that should be opened
diff --git a/src/com/android/contacts/views/detail/ContactDetailFragment.java b/src/com/android/contacts/views/detail/ContactDetailFragment.java
index dacc9cb..eb5c65c 100644
--- a/src/com/android/contacts/views/detail/ContactDetailFragment.java
+++ b/src/com/android/contacts/views/detail/ContactDetailFragment.java
@@ -238,6 +238,7 @@
             }
         });
 
+        mView.setVisibility(View.INVISIBLE);
         return mView;
     }