Moving search UI from ContactsListActivity to the Fragment

Change-Id: I98f811fb178f060376c04fe2bc4037ec9f8e193a
diff --git a/res/layout-finger/search_bar.xml b/res/layout-finger/search_bar.xml
index d322948..304c35d 100644
--- a/res/layout-finger/search_bar.xml
+++ b/res/layout-finger/search_bar.xml
@@ -52,7 +52,7 @@
                 android:scaleType="centerInside" />
               
             <view
-                class="com.android.contacts.SearchEditText"
+                class="com.android.contacts.widget.SearchEditText"
                 android:id="@+id/search_src_text"
                 android:layout_height="wrap_content"
                 android:layout_width="0dip"
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 9681ef0..1eaa6b6 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -123,7 +123,7 @@
  */
 @SuppressWarnings("deprecation")
 public class ContactsListActivity extends Activity implements View.OnCreateContextMenuListener,
-        View.OnClickListener, View.OnKeyListener, TextWatcher, TextView.OnEditorActionListener,
+        View.OnClickListener, View.OnKeyListener,
         OnFocusChangeListener, OnTouchListener, ContactsApplicationController {
 
     private static final String TAG = "ContactsListActivity";
@@ -435,7 +435,6 @@
     private ContactsPreferences mContactsPrefs;
     public int mDisplayOrder;
     private int mSortOrder;
-    private SearchEditText mSearchEditText;
 
     private ContentObserver mProviderStatusObserver = new ContentObserver(new Handler()) {
 
@@ -587,6 +586,10 @@
                     public void onDeleteContactAction(Uri contactUri) {
                         doContactDelete(contactUri);
                     }
+
+                    public void onFinishAction() {
+                        onBackPressed();
+                    }
                 });
                 fragment.setContextMenuAdapter(new ContactBrowseListContextMenuAdapter(fragment));
                 mListFragment = fragment;
@@ -617,6 +620,8 @@
                         setResult(RESULT_OK, intent.setData(contactUri));
                         finish();
                     }
+
+                    // TODO: finish action to support search in the picker
                 });
 
                 mListFragment = fragment;
@@ -705,16 +710,6 @@
         getContentResolver().unregisterContentObserver(mProviderStatusObserver);
     }
 
-
-    /**
-     * Configures search UI.
-     */
-    private void setupSearchView() {
-        mSearchEditText = (SearchEditText)findViewById(R.id.search_src_text);
-        mSearchEditText.addTextChangedListener(this);
-        mSearchEditText.setOnEditorActionListener(this);
-        mSearchEditText.setText(mInitialFilter);
-    }
     public int getSummaryDisplayNameColumnIndex() {
         if (mDisplayOrder == ContactsContract.Preferences.DISPLAY_ORDER_PRIMARY) {
             return SUMMARY_DISPLAY_NAME_PRIMARY_COLUMN_INDEX;
@@ -765,10 +760,6 @@
         // TODO move this to onAttach of the corresponding fragment
         mListView = (ListView) findViewById(android.R.id.list);
 
-        if (mSearchMode) {
-            setupSearchView();
-        }
-
         View emptyView = mListView.getEmptyView();
         if (emptyView instanceof ContactListEmptyView) {
             mEmptyView = (ContactListEmptyView)emptyView;
@@ -786,11 +777,6 @@
             setDefaultMode();
         }
 
-        // See if we were invoked with a filter
-        if (mSearchMode) {
-            mSearchEditText.requestFocus();
-        }
-
         if (!mSearchMode && !checkProviderState(mJustCreated)) {
             return;
         }
@@ -900,13 +886,6 @@
         retryUpgrade.setOnClickListener(listener);
     }
 
-    public String getTextFilter() {
-        if (mSearchEditText != null) {
-            return mSearchEditText.getText().toString();
-        }
-        return null;
-    }
-
     @Override
     protected void onRestart() {
         super.onRestart();
@@ -918,7 +897,7 @@
         // The cursor was killed off in onStop(), so we need to get a new one here
         // We do not perform the query if a filter is set on the list because the
         // filter will cause the query to happen anyway
-        if (TextUtils.isEmpty(getTextFilter())) {
+        if (TextUtils.isEmpty(mListFragment.getQueryString())) {
             startQuery();
         } else {
             // Run the filtered query on the adapter
@@ -1036,15 +1015,13 @@
      * search text edit.
      */
     protected void onSearchTextChanged() {
-        Filter filter = mAdapter.getFilter();
-        filter.filter(getTextFilter());
     }
 
     /**
      * Starts a new activity that will run a search query and display search results.
      */
     protected void doSearch() {
-        String query = getTextFilter();
+        String query = mListFragment.getQueryString();
         if (TextUtils.isEmpty(query)) {
             return;
         }
@@ -1321,33 +1298,6 @@
         return false;
     }
 
-    /**
-     * Event handler for search UI.
-     */
-    public void afterTextChanged(Editable s) {
-        onSearchTextChanged();
-    }
-
-    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(getTextFilter())) {
-                finish();
-            }
-            return true;
-        }
-        return false;
-    }
-
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
         switch (keyCode) {
@@ -1447,18 +1397,6 @@
         return false;
     }
 
-    /**
-     * Dismisses the search UI along with the keyboard if the filter text is empty.
-     */
-    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (mSearchMode && keyCode == KeyEvent.KEYCODE_BACK && TextUtils.isEmpty(getTextFilter())) {
-            hideSoftKeyboard();
-            onBackPressed();
-            return true;
-        }
-        return false;
-    }
-
     public void onListItemClick(int position, long id) {
         if (mSearchMode &&
                 ((ContactItemListAdapter)(mAdapter)).isSearchAllContactsItemPosition(position)) {
@@ -2054,7 +1992,7 @@
         }
 
         String[] projection = getProjectionForQuery();
-        if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
+        if (mSearchMode && TextUtils.isEmpty(mListFragment.getQueryString())) {
             mAdapter.changeCursor(new MatrixCursor(projection));
             return;
         }
@@ -2152,7 +2090,7 @@
      */
     public Cursor doFilter(String filter) {
         String[] projection = getProjectionForQuery();
-        if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
+        if (mSearchMode && TextUtils.isEmpty(mListFragment.getQueryString())) {
             return new MatrixCursor(projection);
         }
 
diff --git a/src/com/android/contacts/JoinContactActivity.java b/src/com/android/contacts/JoinContactActivity.java
index bacd0af..21f5929 100644
--- a/src/com/android/contacts/JoinContactActivity.java
+++ b/src/com/android/contacts/JoinContactActivity.java
@@ -191,7 +191,7 @@
 
             if (mAdapter.getSuggestionsCursorCount() == 0
                     || !mAdapter.isJoinModeShowAllContacts()) {
-                startQuery(getContactFilterUri(getTextFilter()),
+                startQuery(getContactFilterUri(mListFragment.getQueryString()),
                         CONTACTS_SUMMARY_PROJECTION,
                         Contacts._ID + " != " + mTargetContactId
                                 + " AND " + ContactsContract.Contacts.IN_VISIBLE_GROUP + "=1", null,
diff --git a/src/com/android/contacts/MultiplePhonePickerActivity.java b/src/com/android/contacts/MultiplePhonePickerActivity.java
index 492f419..1d1fbbb 100644
--- a/src/com/android/contacts/MultiplePhonePickerActivity.java
+++ b/src/com/android/contacts/MultiplePhonePickerActivity.java
@@ -296,7 +296,7 @@
     @Override
     public Cursor doFilter(String filter) {
         String[] projection = getProjectionForQuery();
-        if (mSearchMode && TextUtils.isEmpty(getTextFilter())) {
+        if (mSearchMode && TextUtils.isEmpty(mListFragment.getQueryString())) {
             return new MatrixCursor(projection);
         }
 
diff --git a/src/com/android/contacts/list/ContactBrowseListFragment.java b/src/com/android/contacts/list/ContactBrowseListFragment.java
index e280779..0fc025e 100644
--- a/src/com/android/contacts/list/ContactBrowseListFragment.java
+++ b/src/com/android/contacts/list/ContactBrowseListFragment.java
@@ -123,4 +123,10 @@
     public void smsContact(Uri contactUri) {
         mListener.onSmsContactAction(contactUri);
     }
+
+    @Override
+    protected void finish() {
+        super.finish();
+        mListener.onFinishAction();
+    }
 }
diff --git a/src/com/android/contacts/list/ContactEntryListAdapter.java b/src/com/android/contacts/list/ContactEntryListAdapter.java
index fc6d1a9..e5b9087 100644
--- a/src/com/android/contacts/list/ContactEntryListAdapter.java
+++ b/src/com/android/contacts/list/ContactEntryListAdapter.java
@@ -50,10 +50,17 @@
     private boolean mNameHighlightingEnabled;
     private ContactPhotoLoader mPhotoLoader;
 
+    // TODO move to Loader
+    protected String mQueryString;
+
     public ContactEntryListAdapter(Context context) {
         super(context);
     }
 
+    public void setQueryString(String queryString) {
+        mQueryString = queryString;
+    }
+
     public Context getContext() {
         return mContext;
     }
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index cc7a1f7..0bf7735 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -22,25 +22,35 @@
 import com.android.contacts.R;
 import com.android.contacts.widget.ContextMenuAdapter;
 import com.android.contacts.widget.PinnedHeaderListView;
+import com.android.contacts.widget.SearchEditText;
+import com.android.contacts.widget.SearchEditText.OnCloseListener;
 
 import android.app.Fragment;
 import android.content.Context;
+import android.text.Editable;
 import android.text.Html;
+import android.text.TextUtils;
+import android.text.TextWatcher;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.View;
 import android.view.ViewGroup;
+import android.view.inputmethod.EditorInfo;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
+import android.widget.Filter;
 import android.widget.ListView;
 import android.widget.TextView;
 import android.widget.AbsListView.OnScrollListener;
+import android.widget.AdapterView.OnItemClickListener;
 
 /**
  * Common base class for various contact-related list fragments.
  */
-public abstract class ContactEntryListFragment extends Fragment
-        implements AdapterView.OnItemClickListener, OnScrollListener {
+public abstract class ContactEntryListFragment extends Fragment implements
+        OnItemClickListener, OnScrollListener, TextWatcher,
+        TextView.OnEditorActionListener, OnCloseListener {
 
     private boolean mSectionHeaderDisplayEnabled;
     private boolean mPhotoLoaderEnabled;
@@ -56,6 +66,7 @@
     private int mDisplayOrder;
     private ContextMenuAdapter mContextMenuAdapter;
     private ContactPhotoLoader mPhotoLoader;
+    private SearchEditText mSearchEditText;
 
     protected abstract View inflateView(LayoutInflater inflater, ViewGroup container);
     protected abstract ContactEntryListAdapter createListAdapter();
@@ -65,6 +76,12 @@
         return mAdapter;
     }
 
+    /**
+     * Override to provide logic that dismisses this fragment.
+     */
+    protected void finish() {
+    }
+
     public void setSectionHeaderDisplayEnabled(boolean flag) {
         mSectionHeaderDisplayEnabled = flag;
     }
@@ -120,7 +137,6 @@
         }
     }
 
-
     @Deprecated
     public void setContactsApplicationController(ContactsApplicationController controller) {
         mAppController = controller;
@@ -179,6 +195,22 @@
 
         configurePinnedHeader();
 
+        if (isPhotoLoaderEnabled()) {
+            mPhotoLoader =
+                new ContactPhotoLoader(getActivity(), R.drawable.ic_contact_list_picture);
+            mAdapter.setPhotoLoader(mPhotoLoader);
+            mListView.setOnScrollListener(this);
+        }
+
+        if (isSearchMode()) {
+            mSearchEditText = (SearchEditText)view.findViewById(R.id.search_src_text);
+            mSearchEditText.setText(getQueryString());
+            mSearchEditText.addTextChangedListener(this);
+            mSearchEditText.setOnEditorActionListener(this);
+            mSearchEditText.setOnCloseListener(this);
+            mAdapter.setQueryString(getQueryString());
+        }
+
         if (isSearchResultsMode()) {
             TextView titleText = (TextView)view.findViewById(R.id.search_results_for);
             if (titleText != null) {
@@ -186,13 +218,6 @@
                         "<b>" + getQueryString() + "</b>")));
             }
         }
-
-        if (isPhotoLoaderEnabled()) {
-            mPhotoLoader =
-                    new ContactPhotoLoader(getActivity(), R.drawable.ic_contact_list_picture);
-            mAdapter.setPhotoLoader(mPhotoLoader);
-            mListView.setOnScrollListener(this);
-        }
     }
 
     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
@@ -213,6 +238,9 @@
         if (isPhotoLoaderEnabled()) {
             mPhotoLoader.resume();
         }
+        if (isSearchMode()) {
+            mSearchEditText.requestFocus();
+        }
     }
 
     @Override
@@ -239,11 +267,49 @@
         onItemClick(position, id);
     }
 
-
     private void hideSoftKeyboard() {
         // Hide soft keyboard, if visible
         InputMethodManager inputMethodManager = (InputMethodManager)
                 getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
         inputMethodManager.hideSoftInputFromWindow(mListView.getWindowToken(), 0);
     }
+
+    /**
+     * Event handler for search UI.
+     */
+    public void afterTextChanged(Editable s) {
+        String query = s.toString().trim();
+        setQueryString(query);
+        mAdapter.setQueryString(query);
+        Filter filter = mAdapter.getFilter();
+        filter.filter(query);
+    }
+
+    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 search UI along with the keyboard if the filter text is empty.
+     */
+    public void onClose() {
+        hideSoftKeyboard();
+        finish();
+    }
 }
diff --git a/src/com/android/contacts/list/ContactItemListAdapter.java b/src/com/android/contacts/list/ContactItemListAdapter.java
index a1d1f12..bd0efa3 100644
--- a/src/com/android/contacts/list/ContactItemListAdapter.java
+++ b/src/com/android/contacts/list/ContactItemListAdapter.java
@@ -101,7 +101,7 @@
      */
     @Override
     public void onContentChanged() {
-        CharSequence constraint = contactsListActivity.getTextFilter();
+        CharSequence constraint = mQueryString;
         if (!TextUtils.isEmpty(constraint)) {
             // Reset the filter state then start an async filter operation
             Filter filter = getFilter();
@@ -123,7 +123,7 @@
         }
 
         if (contactsListActivity.mSearchMode) {
-            return TextUtils.isEmpty(contactsListActivity.getTextFilter());
+            return TextUtils.isEmpty(mQueryString);
         } else if ((contactsListActivity.mMode & ContactsListActivity.MODE_MASK_CREATE_NEW) ==
                 ContactsListActivity.MODE_MASK_CREATE_NEW) {
             // This mode mask adds a header and we always want it to show up, even
@@ -219,7 +219,7 @@
         int count = getRealCount();
 
         if (contactsListActivity.mSearchMode
-                && !TextUtils.isEmpty(contactsListActivity.getTextFilter())) {
+                && !TextUtils.isEmpty(mQueryString)) {
             text = contactsListActivity.getQuantityText(count, R.string.listFoundAllContactsZero,
                     R.plurals.searchFoundContacts);
         } else {
diff --git a/src/com/android/contacts/list/OnContactBrowserActionListener.java b/src/com/android/contacts/list/OnContactBrowserActionListener.java
index b038108..56f9bbc 100644
--- a/src/com/android/contacts/list/OnContactBrowserActionListener.java
+++ b/src/com/android/contacts/list/OnContactBrowserActionListener.java
@@ -67,4 +67,8 @@
      */
     void onSmsContactAction(Uri contactUri);
 
+    /**
+     * Closes the contact browser.
+     */
+    void onFinishAction();
 }
diff --git a/src/com/android/contacts/SearchEditText.java b/src/com/android/contacts/widget/SearchEditText.java
similarity index 80%
rename from src/com/android/contacts/SearchEditText.java
rename to src/com/android/contacts/widget/SearchEditText.java
index 7683f23..1a50976 100644
--- a/src/com/android/contacts/SearchEditText.java
+++ b/src/com/android/contacts/widget/SearchEditText.java
@@ -14,7 +14,7 @@
  * limitations under the License.
  */
 
-package com.android.contacts;
+package com.android.contacts.widget;
 
 import android.content.Context;
 import android.graphics.drawable.Drawable;
@@ -31,12 +31,21 @@
 
     private boolean mMagnifyingGlassShown = true;
     private Drawable mMagnifyingGlass;
+    private OnCloseListener mListener;
+
+    public interface OnCloseListener {
+        void onClose();
+    }
 
     public SearchEditText(Context context, AttributeSet attrs) {
         super(context, attrs);
         mMagnifyingGlass = getCompoundDrawables()[2];
     }
 
+    public void setOnCloseListener(OnCloseListener listener) {
+        this.mListener = listener;
+    }
+
     /**
      * Conditionally shows a magnifying glass icon on the right side of the text field
      * when the text it empty.
@@ -57,13 +66,14 @@
     }
 
     /**
-     * Forwards the onKeyPreIme call to the view's activity.
+     * Dismisses the search UI along with the keyboard if the filter text is empty.
      */
     @Override
     public boolean onKeyPreIme(int keyCode, KeyEvent event) {
-        if (((ContactsListActivity)getContext()).onKeyPreIme(keyCode, event)) {
+        if (keyCode == KeyEvent.KEYCODE_BACK && TextUtils.isEmpty(getText()) && mListener != null) {
+            mListener.onClose();
             return true;
         }
-        return super.onKeyPreIme(keyCode, event);
+        return false;
     }
 }