Breaking search UI out of the contact list fragment

Change-Id: I6d0dbdccaf6a55b380984710989fe8a1f674d3fd
diff --git a/res/layout-finger/contacts_search_content.xml b/res/layout-finger/contacts_search_content.xml
index 680a891..480a8aa 100644
--- a/res/layout-finger/contacts_search_content.xml
+++ b/res/layout-finger/contacts_search_content.xml
@@ -26,9 +26,8 @@
     <include android:id="@+id/searchView"
         layout="@layout/search_bar"/>
 
-    <view
-        class="com.android.contacts.ContactEntryListView" 
-        android:id="@android:id/list"
+    <FrameLayout
+        android:id="@+id/list_container"
         android:layout_width="match_parent"
         android:layout_height="0dip"
         android:layout_weight="1"
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index a015d62..bfb6a96 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -35,6 +35,8 @@
 import com.android.contacts.ui.ContactsPreferencesActivity;
 import com.android.contacts.util.AccountSelectionUtil;
 import com.android.contacts.widget.ContextMenuAdapter;
+import com.android.contacts.widget.SearchEditText;
+import com.android.contacts.widget.SearchEditText.OnFilterTextListener;
 
 import android.accounts.Account;
 import android.app.Activity;
@@ -81,33 +83,25 @@
 
     private static final String TAG = "ContactsListActivity";
 
-    private static final boolean ENABLE_ACTION_ICON_OVERLAYS = true;
-
-    private static final String SHORTCUT_ACTION_KEY = "shortcutAction";
-
     private static final int SUBACTIVITY_NEW_CONTACT = 1;
     private static final int SUBACTIVITY_VIEW_CONTACT = 2;
     private static final int SUBACTIVITY_DISPLAY_GROUP = 3;
     private static final int SUBACTIVITY_SEARCH = 4;
     protected static final int SUBACTIVITY_FILTER = 5;
 
-    public static final String AUTHORITIES_FILTER_KEY = "authorities";
-
-    static final String[] RAW_CONTACTS_PROJECTION = new String[] {
+    private static final String[] RAW_CONTACTS_PROJECTION = new String[] {
         RawContacts._ID, //0
         RawContacts.CONTACT_ID, //1
         RawContacts.ACCOUNT_TYPE, //2
     };
 
-    static final String KEY_PICKER_MODE = "picker_mode";
-
     private Uri mSelectedContactUri;
 
     private ArrayList<Long> mWritableRawContactIds = new ArrayList<Long>();
     private int  mWritableSourcesCnt;
     private int  mReadOnlySourcesCnt;
 
-    final String[] sLookupProjection = new String[] {
+    private final String[] sLookupProjection = new String[] {
             Contacts.LOOKUP_KEY
     };
     private class DeleteClickListener implements DialogInterface.OnClickListener {
@@ -128,6 +122,7 @@
     private boolean mSearchInitiated;
 
     private ContactsRequest mRequest;
+    private SearchEditText mSearchEditText;
 
     public ContactsListActivity() {
         mIntentResolver = new ContactsIntentResolver(this);
@@ -164,11 +159,41 @@
 
         onCreateFragment();
 
+        int listFragmentContainerId;
+        if (mRequest.isSearchMode()) {
+            setContentView(R.layout.contacts_search_content);
+            listFragmentContainerId = R.id.list_container;
+            setupSearchUI();
+        } else {
+            listFragmentContainerId = android.R.id.content;
+        }
         FragmentTransaction transaction = openFragmentTransaction();
-        transaction.add(mListFragment, android.R.id.content);
+        transaction.add(mListFragment, listFragmentContainerId);
         transaction.commit();
     }
 
+    private void setupSearchUI() {
+        mSearchEditText = (SearchEditText)findViewById(R.id.search_src_text);
+        mSearchEditText.setText(mRequest.getQueryString());
+        mSearchEditText.setOnFilterTextListener(new OnFilterTextListener() {
+            public void onFilterChange(String queryString) {
+                mListFragment.setQueryString(queryString);
+            }
+
+            public void onCancelSearch() {
+                finish();
+            }
+        });
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        if (mRequest.isSearchMode()) {
+            mSearchEditText.requestFocus();
+        }
+    }
+
     /**
      * Creates the fragment based on the current request.
      */
@@ -466,7 +491,7 @@
             }
             case R.id.menu_accounts: {
                 final Intent intent = new Intent(Settings.ACTION_SYNC_SETTINGS);
-                intent.putExtra(AUTHORITIES_FILTER_KEY, new String[] {
+                intent.putExtra(Settings.EXTRA_AUTHORITIES, new String[] {
                     ContactsContract.AUTHORITY
                 });
                 startActivity(intent);
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index c894ba7..1f8822e 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -22,8 +22,6 @@
 import com.android.contacts.R;
 import com.android.contacts.ui.ContactsPreferences;
 import com.android.contacts.widget.ContextMenuAdapter;
-import com.android.contacts.widget.SearchEditText;
-import com.android.contacts.widget.SearchEditText.OnCloseListener;
 
 import android.accounts.Account;
 import android.accounts.AccountManager;
@@ -36,14 +34,12 @@
 import android.content.Context;
 import android.content.IContentService;
 import android.content.Intent;
-import android.content.SharedPreferences;
 import android.database.ContentObserver;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.os.Handler;
 import android.os.Parcelable;
 import android.os.RemoteException;
-import android.preference.PreferenceManager;
 import android.provider.ContactsContract;
 import android.provider.Settings;
 import android.provider.ContactsContract.ProviderStatus;
@@ -51,7 +47,6 @@
 import android.text.Editable;
 import android.text.Html;
 import android.text.TextUtils;
-import android.text.TextWatcher;
 import android.util.Log;
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
@@ -70,16 +65,13 @@
 import android.widget.TextView;
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.AdapterView.OnItemClickListener;
-import android.widget.TextView.OnEditorActionListener;
 
 /**
  * Common base class for various contact-related list fragments.
  */
 public abstract class ContactEntryListFragment<T extends ContactEntryListAdapter>
         extends LoaderManagingFragment<Cursor>
-        implements OnItemClickListener,
-        OnScrollListener, TextWatcher, OnEditorActionListener, OnCloseListener,
-        OnFocusChangeListener, OnTouchListener {
+        implements OnItemClickListener, OnScrollListener, OnFocusChangeListener, OnTouchListener {
 
     private static final String TAG = "ContactEntryListFragment";
 
@@ -107,7 +99,6 @@
 
     private ContextMenuAdapter mContextMenuAdapter;
     private ContactPhotoLoader mPhotoLoader;
-    private SearchEditText mSearchEditText;
     private ContactListEmptyView mEmptyView;
     private ProviderStatusLoader mProviderStatusLoader;
     private ContactsPreferences mContactsPrefs;
@@ -132,6 +123,7 @@
         return mAdapter;
     }
 
+    @Override
     public View getView() {
         return mView;
     }
@@ -221,7 +213,6 @@
 
     public void setSearchMode(boolean flag) {
         mSearchMode = flag;
-        configureSearchEditText();
     }
 
     public boolean isSearchMode() {
@@ -230,7 +221,6 @@
 
     public void setSearchResultsMode(boolean flag) {
         mSearchResultsMode = flag;
-        configureSearchEditText();
     }
 
     public boolean isSearchResultsMode() {
@@ -242,9 +232,12 @@
     }
 
     public void setQueryString(String queryString) {
-        mQueryString = queryString;
-        if (mAdapter != null) {
-            mAdapter.setQueryString(queryString);
+        if (!TextUtils.equals(mQueryString, queryString)) {
+            mQueryString = queryString;
+            if (mAdapter != null) {
+                mAdapter.setQueryString(queryString);
+                reloadData();
+            }
         }
     }
 
@@ -324,7 +317,7 @@
             mListState = savedState.getParcelable(LIST_STATE_KEY);
         }
     }
-    
+
     @Override
     public View onCreateView(LayoutInflater inflater, ViewGroup container,
             Bundle savedInstanceState) {
@@ -372,7 +365,6 @@
         }
 
         configurePhotoLoader();
-        configureSearchEditText();
         configureSearchResultText();
         return mView;
     }
@@ -401,15 +393,6 @@
             }
         }
     }
-    protected void configureSearchEditText() {
-        if (isSearchMode() && mView != null) {
-            mSearchEditText = (SearchEditText)mView.findViewById(R.id.search_src_text);
-            mSearchEditText.setText(getQueryString());
-            mSearchEditText.addTextChangedListener(this);
-            mSearchEditText.setOnEditorActionListener(this);
-            mSearchEditText.setOnCloseListener(this);
-        }
-    }
 
     protected void configureAdapter() {
         if (mAdapter != null) {
@@ -461,9 +444,6 @@
         if (isPhotoLoaderEnabled()) {
             mPhotoLoader.resume();
         }
-        if (isSearchMode()) {
-            mSearchEditText.requestFocus();
-        }
     }
 
     @Override
@@ -491,35 +471,6 @@
     }
 
     /**
-     * Event handler for search UI.
-     */
-    public void afterTextChanged(Editable s) {
-        String query = s.toString().trim();
-        setQueryString(query);
-        reloadData();
-    }
-
-    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-    }
-
-    public void onTextChanged(CharSequence s, int start, int before, int count) {
-    }
-
-    /**
-     * Event handler for search UI.
-     */
-    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
-        if (actionId == EditorInfo.IME_ACTION_DONE) {
-            hideSoftKeyboard();
-            if (TextUtils.isEmpty(getQueryString())) {
-                finish();
-            }
-            return true;
-        }
-        return false;
-    }
-
-    /**
      * Dismisses the soft keyboard when the list takes focus.
      */
     public void onFocusChange(View view, boolean hasFocus) {
diff --git a/src/com/android/contacts/list/ContactsIntentResolver.java b/src/com/android/contacts/list/ContactsIntentResolver.java
index 62d56b5..8388daa 100644
--- a/src/com/android/contacts/list/ContactsIntentResolver.java
+++ b/src/com/android/contacts/list/ContactsIntentResolver.java
@@ -37,8 +37,8 @@
 import android.util.Log;
 
 /**
- * Maintains contact list configuration, which is a transient object that
- * deals with intents, saved instance configuration etc.
+ * Parses a Contacts intent, extracting all relevant parts and packaging them
+ * as a {@link ContactsRequest} object.
  */
 @SuppressWarnings("deprecation")
 public class ContactsIntentResolver {
diff --git a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
index 45380bc..13d60e1 100644
--- a/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/DefaultContactBrowseListFragment.java
@@ -124,9 +124,7 @@
 
     @Override
     protected View inflateView(LayoutInflater inflater, ViewGroup container) {
-        if (isSearchMode()) {
-            return inflater.inflate(R.layout.contacts_search_content, null);
-        } else if (isSearchResultsMode()) {
+        if (isSearchResultsMode()) {
             return inflater.inflate(R.layout.contacts_list_search_results, null);
         } else {
             return inflater.inflate(R.layout.contacts_list_content, null);
diff --git a/src/com/android/contacts/widget/SearchEditText.java b/src/com/android/contacts/widget/SearchEditText.java
index 1a50976..45001a5 100644
--- a/src/com/android/contacts/widget/SearchEditText.java
+++ b/src/com/android/contacts/widget/SearchEditText.java
@@ -18,31 +18,40 @@
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
+import android.text.Editable;
 import android.text.TextUtils;
+import android.text.TextWatcher;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
+import android.view.inputmethod.EditorInfo;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.TextView.OnEditorActionListener;
 
 /**
  * A custom text editor that helps automatically dismiss the activity along with the soft
  * keyboard.
  */
-public class SearchEditText extends EditText {
-
+public class SearchEditText extends EditText implements OnEditorActionListener, TextWatcher {
     private boolean mMagnifyingGlassShown = true;
-    private Drawable mMagnifyingGlass;
-    private OnCloseListener mListener;
 
-    public interface OnCloseListener {
-        void onClose();
+    private Drawable mMagnifyingGlass;
+    private OnFilterTextListener mListener;
+
+    public interface OnFilterTextListener {
+        void onFilterChange(String queryString);
+        void onCancelSearch();
     }
 
     public SearchEditText(Context context, AttributeSet attrs) {
         super(context, attrs);
+        addTextChangedListener(this);
+        setOnEditorActionListener(this);
         mMagnifyingGlass = getCompoundDrawables()[2];
     }
 
-    public void setOnCloseListener(OnCloseListener listener) {
+    public void setOnFilterTextListener(OnFilterTextListener listener) {
         this.mListener = listener;
     }
 
@@ -71,9 +80,51 @@
     @Override
     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
         if (keyCode == KeyEvent.KEYCODE_BACK && TextUtils.isEmpty(getText()) && mListener != null) {
-            mListener.onClose();
+            mListener.onCancelSearch();
             return true;
         }
         return false;
     }
+
+    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+    }
+
+    @Override
+    public void onTextChanged(CharSequence s, int start, int before, int count) {
+    }
+
+    /**
+     * Event handler for search UI.
+     */
+    public void afterTextChanged(Editable s) {
+        if (mListener != null) {
+            mListener.onFilterChange(trim(s));
+        }
+    }
+
+    private String trim(Editable s) {
+        return s.toString().trim();
+    }
+
+    /**
+     * Event handler for search UI.
+     */
+    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
+        if (actionId == EditorInfo.IME_ACTION_DONE) {
+            hideSoftKeyboard();
+            if (TextUtils.isEmpty(trim(getText())) && mListener != null) {
+                mListener.onCancelSearch();
+            }
+            return true;
+        }
+        return false;
+    }
+
+    private void hideSoftKeyboard() {
+        // Hide soft keyboard, if visible
+        InputMethodManager inputMethodManager = (InputMethodManager)
+                getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
+        inputMethodManager.hideSoftInputFromWindow(getWindowToken(), 0);
+    }
+
 }