Use EditText for search in ActionBar

SearchView is too limited in terms of customizability for the
kind of UI styling we are trying to do. All we really need is a
space to enter text into, so use a EditText embedded in a custom
ActionBar view instead.

Bug: 13932490

Change-Id: I7081684c46b29b416dd6d0e5fcff249ee6f00405
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 7795aa8..6879f3a 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -122,11 +122,6 @@
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.TAB" />
             </intent-filter>
-            <intent-filter>
-                <action android:name="android.intent.action.SEARCH" />
-            </intent-filter>
-            <meta-data android:name="android.app.searchable"
-                android:resource="@xml/searchable"/>
         </activity>
 
         <activity android:name="com.android.dialer.calllog.CallLogActivity"
diff --git a/res/layout/dialtacts_activity.xml b/res/layout/dialtacts_activity.xml
index 6d04103..2cd796b 100644
--- a/res/layout/dialtacts_activity.xml
+++ b/res/layout/dialtacts_activity.xml
@@ -46,53 +46,6 @@
             android:id="@+id/search_and_remove_view_container"
             android:visibility="gone"
             >
-            <!--  TODO: This is set to visibility:gone for now, should be removed entirely -->
-            <LinearLayout
-                android:layout_width="match_parent"
-                android:layout_height="@dimen/search_box_height"
-                android:id="@+id/search_view_container"
-                android:orientation="horizontal"
-                android:layout_marginTop="@dimen/search_top_margin"
-                android:layout_marginBottom="@dimen/search_bottom_margin"
-                android:layout_marginLeft="@dimen/search_margin_horizontal"
-                android:layout_marginRight="@dimen/search_margin_horizontal"
-                android:paddingLeft="@dimen/search_box_left_padding"
-                android:paddingRight="@dimen/search_box_right_padding"
-                android:background="@drawable/search_bg"
-                android:gravity="center_vertical"
-                android:visibility="gone"
-                >
-                <EditText
-                    android:id="@+id/search_view"
-                    android:layout_width="match_parent"
-                    android:layout_height="@dimen/search_box_icon_size"
-                    android:layout_weight="1"
-                    android:layout_marginLeft="@dimen/search_box_text_left_margin"
-                    android:textSize="@dimen/search_text_size"
-                    android:fontFamily="@string/search_font_family"
-                    android:textColor="@color/searchbox_text_color"
-                    android:textColorHint="@color/searchbox_hint_text_color"
-                    android:inputType="textFilter"/>
-                <ImageView
-                    android:id="@+id/search_close_button"
-                    android:layout_height="@dimen/search_box_icon_size"
-                    android:layout_width="@dimen/search_box_icon_size"
-                    android:padding="6dp"
-                    android:src="@drawable/ic_close_dk"
-                    android:clickable="true"
-                    android:background="?android:attr/selectableItemBackground"
-                    android:contentDescription="@string/description_clear_search"
-                    android:visibility="gone" />
-                <ImageView
-                    android:id="@+id/voice_search_button"
-                    android:layout_height="@dimen/search_box_icon_size"
-                    android:layout_width="@dimen/search_box_icon_size"
-                    android:padding="@dimen/search_box_icon_padding"
-                    android:src="@drawable/ic_voice_search"
-                    android:clickable="true"
-                    android:contentDescription="@string/description_start_voice_search"
-                    android:background="?android:attr/selectableItemBackground" />
-            </LinearLayout>
             <com.android.dialer.list.RemoveView
                 android:layout_width="match_parent"
                 android:layout_height="56dp"
diff --git a/res/layout/search_edittext.xml b/res/layout/search_edittext.xml
new file mode 100644
index 0000000..236d2bf
--- /dev/null
+++ b/res/layout/search_edittext.xml
@@ -0,0 +1,48 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:id="@+id/search_view_container"
+    android:orientation="horizontal"
+    android:layout_marginTop="@dimen/search_top_margin"
+    android:layout_marginBottom="@dimen/search_bottom_margin"
+    android:layout_marginLeft="@dimen/search_margin_horizontal"
+    android:layout_marginRight="@dimen/search_margin_horizontal"
+    android:paddingLeft="@dimen/search_box_left_padding"
+    android:paddingRight="@dimen/search_box_right_padding"
+    android:background="@drawable/search_bg"
+    android:gravity="center_vertical"
+    >
+    <EditText
+        android:id="@+id/search_view"
+        android:layout_width="0dp"
+        android:layout_height="@dimen/search_box_icon_size"
+        android:layout_weight="1"
+        android:layout_marginLeft="@dimen/search_box_text_left_margin"
+        android:textSize="@dimen/search_text_size"
+        android:fontFamily="@string/search_font_family"
+        android:textColor="@color/searchbox_text_color"
+        android:textColorHint="@color/searchbox_hint_text_color"
+        android:hint="@string/dialer_hint_find_contact"
+        android:inputType="textFilter"/>
+    <ImageView
+        android:id="@+id/search_close_button"
+        android:layout_height="@dimen/search_box_icon_size"
+        android:layout_width="@dimen/search_box_icon_size"
+        android:padding="6dp"
+        android:src="@drawable/ic_close_dk"
+        android:clickable="true"
+        android:background="?android:attr/selectableItemBackground"
+        android:contentDescription="@string/description_clear_search"
+        android:visibility="gone" />
+    <ImageView
+        android:id="@+id/voice_search_button"
+        android:layout_height="@dimen/search_box_icon_size"
+        android:layout_width="@dimen/search_box_icon_size"
+        android:padding="@dimen/search_box_icon_padding"
+        android:src="@drawable/ic_voice_search"
+        android:clickable="true"
+        android:contentDescription="@string/description_start_voice_search"
+        android:background="?android:attr/selectableItemBackground" />
+</LinearLayout>
\ No newline at end of file
diff --git a/res/menu/dialtacts_options.xml b/res/menu/dialtacts_options.xml
index a332030..17f4d9f 100644
--- a/res/menu/dialtacts_options.xml
+++ b/res/menu/dialtacts_options.xml
@@ -14,16 +14,11 @@
      limitations under the License.
 -->
 <menu xmlns:android="http://schemas.android.com/apk/res/android">
-    <item
-        android:id="@+id/menu_search"
-        android:title="@string/description_search_button"
-        android:showAsAction="always"
-        android:actionViewClass="android.widget.SearchView"/>
+
     <item
         android:id="@+id/menu_history"
         android:icon="@drawable/ic_menu_history_lt"
-        android:title="@string/action_menu_call_history_description"
-        android:showAsAction="ifRoom"/>
+        android:title="@string/action_menu_call_history_description" />
     <item
         android:id="@+id/menu_import_export"
         android:title="@string/menu_import_export" />
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 66b36a7..57fbe82 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -113,11 +113,9 @@
     <!-- Margin to the left and right of the search box. -->
     <dimen name="search_margin_horizontal">7dp</dimen>
     <!-- Margin above the search box. -->
-    <dimen name="search_top_margin">10dp</dimen>
+    <dimen name="search_top_margin">4dp</dimen>
     <!-- Margin below the search box. -->
     <dimen name="search_bottom_margin">4dp</dimen>
-    <!-- Height of the search box. -->
-    <dimen name="search_box_height">41dp</dimen>
     <!-- Search box text size -->
     <dimen name="search_text_size">13.24sp</dimen>
     <!-- Search box interior padding - left -->
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 2e0225a..4e96e3e 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -20,18 +20,17 @@
 import android.animation.LayoutTransition;
 import android.animation.Animator.AnimatorListener;
 import android.animation.AnimatorListenerAdapter;
+import android.app.ActionBar;
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.FragmentManager;
 import android.app.FragmentTransaction;
-import android.app.SearchManager;
 import android.content.ActivityNotFoundException;
 import android.content.Context;
 import android.content.Intent;
 import android.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
 import android.content.res.TypedArray;
-import android.graphics.Outline;
 import android.net.Uri;
 import android.os.Bundle;
 import android.os.RemoteException;
@@ -41,28 +40,23 @@
 import android.speech.RecognizerIntent;
 import android.support.v4.view.ViewPager;
 import android.telephony.TelephonyManager;
+import android.text.Editable;
 import android.text.TextUtils;
+import android.text.TextWatcher;
 import android.util.Log;
-import android.view.Gravity;
 import android.view.Menu;
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.View.OnClickListener;
-import android.view.View.OnLayoutChangeListener;
-import android.view.ViewGroup.LayoutParams;
-import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.animation.AccelerateInterpolator;
 import android.view.animation.DecelerateInterpolator;
 import android.view.animation.Interpolator;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.AbsListView.OnScrollListener;
-import android.widget.FrameLayout;
-import android.widget.LinearLayout;
+import android.widget.EditText;
 import android.widget.PopupMenu;
 import android.widget.RelativeLayout;
 import android.widget.SearchView;
-import android.widget.SearchView.OnQueryTextListener;
 import android.widget.Toast;
 
 import com.android.contacts.common.CallUtil;
@@ -82,7 +76,6 @@
 import com.android.dialer.list.OnDragDropListener;
 import com.android.dialer.list.OnListFragmentScrolledListener;
 import com.android.dialer.list.SpeedDialFragment;
-import com.android.dialer.list.PhoneFavoriteTileView;
 import com.android.dialer.list.PhoneFavoriteSquareTileView;
 import com.android.dialer.list.RegularSearchFragment;
 import com.android.dialer.list.RemoveView;
@@ -195,7 +188,9 @@
     // This view points to the Framelayout that houses both the search view and remove view
     // containers.
     private View mSearchAndRemoveViewContainer;
-    private SearchView mSearchView;
+    private EditText mSearchView;
+    private View mSearchViewCloseButton;
+    private View mVoiceSearchButton;
     /**
      * View that contains the "Remove" dialog that shows up when the user long presses a contact.
      * If the user releases a contact when hovering on top of this, the contact is unfavorited and
@@ -260,19 +255,19 @@
     /**
      * Listener used to send search queries to the phone search fragment.
      */
-    private final OnQueryTextListener mPhoneSearchQueryTextListener = new OnQueryTextListener() {
+    private final TextWatcher mPhoneSearchQueryTextListener = new TextWatcher() {
             @Override
-            public boolean onQueryTextSubmit(String query) {
-                return false;
+            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
             }
 
             @Override
-            public boolean onQueryTextChange(String newText) {
+            public void onTextChanged(CharSequence s, int start, int before, int count) {
+                final String newText = s.toString();
                 if (newText.equals(mSearchQuery)) {
                     // If the query hasn't changed (perhaps due to activity being destroyed
                     // and restored, or user launching the same DIAL intent twice), then there is
                     // no need to do anything here.
-                    return true;
+                    return;
                 }
                 mSearchQuery = newText;
                 if (DEBUG) {
@@ -283,7 +278,9 @@
                 // Show search result with non-empty text. Show a bare list otherwise.
                 if (TextUtils.isEmpty(newText) && getInSearchUi()) {
                     exitSearchUi();
-                    return true;
+                    mSearchViewCloseButton.setVisibility(View.GONE);
+                    mVoiceSearchButton.setVisibility(View.VISIBLE);
+                    return;
                 } else if (!TextUtils.isEmpty(newText)) {
                     final boolean sameSearchMode = (dialpadSearch && mInDialpadSearch) ||
                             (!dialpadSearch && mInRegularSearch);
@@ -298,9 +295,15 @@
                     } else if (mRegularSearchFragment != null) {
                         mRegularSearchFragment.setQueryString(newText, false);
                     }
-                    return true;
+                    mSearchViewCloseButton.setVisibility(View.VISIBLE);
+                    mVoiceSearchButton.setVisibility(View.GONE);
+                    return;
                 }
-                return true;
+                return;
+            }
+
+            @Override
+            public void afterTextChanged(Editable s) {
             }
     };
 
@@ -316,8 +319,17 @@
         setContentView(R.layout.dialtacts_activity);
         getWindow().setBackgroundDrawable(null);
 
-        getActionBar().setDisplayShowHomeEnabled(false);
-        getActionBar().setDisplayShowTitleEnabled(false);
+        final ActionBar actionBar = getActionBar();
+        actionBar.setCustomView(R.layout.search_edittext);
+        actionBar.setDisplayShowCustomEnabled(true);
+
+        final View customView = actionBar.getCustomView();
+
+        mSearchView = (EditText) customView.findViewById(R.id.search_view);
+        mSearchView.addTextChangedListener(mPhoneSearchQueryTextListener);
+
+        mSearchViewCloseButton = customView.findViewById(R.id.search_close_button);
+        mSearchViewCloseButton.setOnClickListener(this);
 
         final TypedArray styledAttributes = getTheme().obtainStyledAttributes(
                 new int[] { android.R.attr.actionBarSize });
@@ -368,6 +380,7 @@
             mInCallDialpadUp = false;
         }
         mFirstLaunch = false;
+        prepareVoiceSearchButton();
         mDialerDatabaseHelper.startSmartDialUpdateThread();
     }
 
@@ -442,9 +455,9 @@
                 break;
             case R.id.search_close_button:
                 // Clear the search field
-                if (!TextUtils.isEmpty(mSearchView.getQuery())) {
+                if (!TextUtils.isEmpty(mSearchView.getText())) {
                     mDialpadFragment.clearDialpad();
-                    mSearchView.setQuery("", false);
+                    mSearchView.setText(null);
                 }
                 break;
             case R.id.voice_search_button:
@@ -523,7 +536,7 @@
                         RecognizerIntent.EXTRA_RESULTS);
                 if (matches.size() > 0) {
                     final String match = matches.get(0);
-                    mSearchView.setQuery(match, false);
+                    mSearchView.setText(match);
                 } else {
                     Log.e(TAG, "Voice search - nothing heard");
                 }
@@ -578,7 +591,7 @@
     }
 
     private void hideDialpadAndSearchUi() {
-        mSearchView.setQuery("", false);
+        mSearchView.setText(null);
         hideDialpadFragment(false, true);
     }
 
@@ -641,6 +654,17 @@
         }
     }
 
+    private void prepareVoiceSearchButton() {
+        mVoiceSearchButton = getActionBar().getCustomView().findViewById(R.id.voice_search_button);
+        final Intent voiceIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
+        if (canIntentBeHandled(voiceIntent)) {
+            mVoiceSearchButton.setVisibility(View.VISIBLE);
+            mVoiceSearchButton.setOnClickListener(this);
+        } else {
+            mVoiceSearchButton.setVisibility(View.GONE);
+        }
+    }
+
     @Override
     public boolean onCreateOptionsMenu(Menu menu) {
         if (DEBUG) {
@@ -648,15 +672,9 @@
         }
         MenuInflater inflater = getMenuInflater();
         inflater.inflate(R.menu.dialtacts_options, menu);
-        final MenuItem searchItem = menu.findItem(R.id.menu_search);
-        mSearchView = (SearchView) searchItem.getActionView();
-        SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);
-        mSearchView.setSearchableInfo(searchManager.getSearchableInfo(getComponentName()));
-        mSearchView.setOnQueryTextListener(mPhoneSearchQueryTextListener);
-        mSearchView.setIconifiedByDefault(false);
 
         if (mPendingSearchViewQuery != null) {
-            mSearchView.setQuery(mPendingSearchViewQuery, false);
+            mSearchView.setText(mPendingSearchViewQuery);
             mPendingSearchViewQuery = null;
         }
         return super.onCreateOptionsMenu(menu);
@@ -823,7 +841,7 @@
         if (mDialpadFragment != null && mDialpadFragment.isVisible()) {
             hideDialpadFragment(true, false);
         } else if (getInSearchUi()) {
-            mSearchView.setQuery(null, false);
+            mSearchView.setText(null);
             mDialpadFragment.clearDialpad();
         } else {
             super.onBackPressed();
@@ -837,13 +855,8 @@
         }
         final String normalizedQuery = SmartDialNameMatcher.normalizeNumber(query,
                 SmartDialNameMatcher.LATIN_SMART_DIAL_MAP);
-        if (mSearchView == null) {
-            if (!TextUtils.isEmpty(normalizedQuery)) {
-                mPendingSearchViewQuery = normalizedQuery;
-            }
-            return;
-        }
-        if (!TextUtils.equals(mSearchView.getQuery(), normalizedQuery)) {
+
+        if (!TextUtils.equals(mSearchView.getText(), normalizedQuery)) {
             if (DEBUG) {
                 Log.d(TAG, "onDialpadQueryChanged - new query: " + query);
             }
@@ -853,9 +866,12 @@
                 // that would bring the user back to the search fragment regardless of the
                 // previous state of the application. Instead, just return here and let the
                 // fragment manager correctly figure out whatever fragment was last displayed.
+                if (!TextUtils.isEmpty(normalizedQuery)) {
+                    mPendingSearchViewQuery = normalizedQuery;
+                }
                 return;
             }
-            mSearchView.setQuery(normalizedQuery, false);
+            mSearchView.setText(normalizedQuery);
         }
     }