diff --git a/core/java/android/app/SearchDialog.java b/core/java/android/app/SearchDialog.java index 42eda0212e017..8e2d360b5637a 100644 --- a/core/java/android/app/SearchDialog.java +++ b/core/java/android/app/SearchDialog.java @@ -168,6 +168,7 @@ public class SearchDialog extends Dialog { SearchBar searchBar = (SearchBar) findViewById(com.android.internal.R.id.search_bar); searchBar.setSearchDialog(this); mSearchView = (SearchView) findViewById(com.android.internal.R.id.search_view); + mSearchView.setIconified(false); mSearchView.setOnCloseListener(mOnCloseListener); mSearchView.setOnQueryTextListener(mOnQueryChangeListener); mSearchView.setOnSuggestionListener(mOnSuggestionSelectionListener); @@ -632,31 +633,6 @@ public class SearchDialog extends Dialog { mSearchDialog = searchDialog; } - /** - * Overrides the handling of the back key to move back to the previous - * sources or dismiss the search dialog, instead of dismissing the input - * method. - */ - @Override - public boolean dispatchKeyEventPreIme(KeyEvent event) { - if (DBG) - Log.d(LOG_TAG, "onKeyPreIme(" + event + ")"); - if (mSearchDialog != null && event.getKeyCode() == KeyEvent.KEYCODE_BACK) { - KeyEvent.DispatcherState state = getKeyDispatcherState(); - if (state != null) { - if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { - state.startTracking(event, this); - return true; - } else if (event.getAction() == KeyEvent.ACTION_UP && !event.isCanceled() - && state.isTracking(event)) { - mSearchDialog.onBackPressed(); - return true; - } - } - } - return super.dispatchKeyEventPreIme(event); - } - /** * Don't allow action modes in a SearchBar, it looks silly. */ diff --git a/core/java/android/content/SearchRecentSuggestionsProvider.java b/core/java/android/content/SearchRecentSuggestionsProvider.java index 3d89e92d43485..e1a8d21f99702 100644 --- a/core/java/android/content/SearchRecentSuggestionsProvider.java +++ b/core/java/android/content/SearchRecentSuggestionsProvider.java @@ -186,6 +186,9 @@ public class SearchRecentSuggestionsProvider extends ContentProvider { mSuggestionProjection = new String [] { "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT, + "'android.resource://system/" + + com.android.internal.R.drawable.ic_menu_recent_history + "' AS " + + SearchManager.SUGGEST_COLUMN_ICON_1, "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, "display2 AS " + SearchManager.SUGGEST_COLUMN_TEXT_2, "query AS " + SearchManager.SUGGEST_COLUMN_QUERY, @@ -196,6 +199,9 @@ public class SearchRecentSuggestionsProvider extends ContentProvider { mSuggestionProjection = new String [] { "0 AS " + SearchManager.SUGGEST_COLUMN_FORMAT, + "'android.resource://system/" + + com.android.internal.R.drawable.ic_menu_recent_history + "' AS " + + SearchManager.SUGGEST_COLUMN_ICON_1, "display1 AS " + SearchManager.SUGGEST_COLUMN_TEXT_1, "query AS " + SearchManager.SUGGEST_COLUMN_QUERY, "_id" diff --git a/core/java/android/widget/SearchView.java b/core/java/android/widget/SearchView.java index f3bda439fbe85..b2d1a1e535736 100644 --- a/core/java/android/widget/SearchView.java +++ b/core/java/android/widget/SearchView.java @@ -18,8 +18,6 @@ package android.widget; import static android.widget.SuggestionsAdapter.getColumnString; -import com.android.internal.R; - import android.app.PendingIntent; import android.app.SearchManager; import android.app.SearchableInfo; @@ -39,10 +37,14 @@ import android.net.Uri; import android.os.Bundle; import android.speech.RecognizerIntent; import android.text.Editable; +import android.text.Spannable; +import android.text.SpannableStringBuilder; import android.text.TextUtils; import android.text.TextWatcher; +import android.text.style.ImageSpan; import android.util.AttributeSet; import android.util.Log; +import android.util.TypedValue; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; @@ -51,6 +53,8 @@ import android.widget.AdapterView.OnItemClickListener; import android.widget.AdapterView.OnItemSelectedListener; import android.widget.TextView.OnEditorActionListener; +import com.android.internal.R; + import java.util.WeakHashMap; /** @@ -87,6 +91,8 @@ public class SearchView extends LinearLayout { private View mSearchEditFrame; private View mVoiceButton; private SearchAutoComplete mQueryTextView; + private View mDropDownAnchor; + private ImageView mSearchHintIcon; private boolean mSubmitButtonEnabled; private CharSequence mQueryHint; private boolean mQueryRefinement; @@ -195,6 +201,7 @@ public class SearchView extends LinearLayout { mSubmitButton = findViewById(R.id.search_go_btn); mCloseButton = (ImageView) findViewById(R.id.search_close_btn); mVoiceButton = findViewById(R.id.search_voice_btn); + mSearchHintIcon = (ImageView) findViewById(R.id.search_mag_icon); mSearchButton.setOnClickListener(mOnClickListener); mCloseButton.setOnClickListener(mOnClickListener); @@ -244,7 +251,20 @@ public class SearchView extends LinearLayout { mVoiceAppSearchIntent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH); mVoiceAppSearchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); + mDropDownAnchor = findViewById(mQueryTextView.getDropDownAnchor()); + if (mDropDownAnchor != null) { + mDropDownAnchor.addOnLayoutChangeListener(new OnLayoutChangeListener() { + @Override + public void onLayoutChange(View v, int left, int top, int right, int bottom, + int oldLeft, int oldTop, int oldRight, int oldBottom) { + adjustDropDownSizeAndPosition(); + } + + }); + } + updateViewsVisibility(mIconifiedByDefault); + updateQueryHint(); } /** @@ -263,7 +283,7 @@ public class SearchView extends LinearLayout { } // Cache the voice search capability mVoiceButtonEnabled = hasVoiceSearch(); - updateViewsVisibility(mIconifiedByDefault); + updateViewsVisibility(isIconified()); } /** @@ -300,7 +320,6 @@ public class SearchView extends LinearLayout { mQueryTextView.clearFocus(); setImeVisibility(false); mClearingFocus = false; - updateViewsVisibility(mIconifiedByDefault); } /** @@ -555,6 +574,7 @@ public class SearchView extends LinearLayout { mSearchButton.setVisibility(visCollapsed); updateSubmitButton(hasText); mSearchEditFrame.setVisibility(collapsed ? GONE : VISIBLE); + mSearchHintIcon.setVisibility(mIconifiedByDefault ? GONE : VISIBLE); updateCloseButton(); updateVoiceButton(!hasText); updateSubmitArea(); @@ -822,9 +842,29 @@ public class SearchView extends LinearLayout { return result; } + private int getSearchIconId() { + TypedValue outValue = new TypedValue(); + getContext().getTheme().resolveAttribute(com.android.internal.R.attr.searchViewSearchIcon, + outValue, true); + return outValue.resourceId; + } + + private CharSequence getDecoratedHint(CharSequence hintText) { + // If the field is always expanded, then don't add the search icon to the hint + if (!mIconifiedByDefault) return hintText; + + SpannableStringBuilder ssb = new SpannableStringBuilder(" "); // for the icon + ssb.append(hintText); + Drawable searchIcon = getContext().getResources().getDrawable(getSearchIconId()); + int textSize = (int) (mQueryTextView.getTextSize() * 1.25); + searchIcon.setBounds(0, 0, textSize, textSize); + ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + return ssb; + } + private void updateQueryHint() { if (mQueryHint != null) { - mQueryTextView.setHint(mQueryHint); + mQueryTextView.setHint(getDecoratedHint(mQueryHint)); } else if (mSearchable != null) { CharSequence hint = null; int hintId = mSearchable.getHintId(); @@ -832,8 +872,10 @@ public class SearchView extends LinearLayout { hint = getContext().getString(hintId); } if (hint != null) { - mQueryTextView.setHint(hint); + mQueryTextView.setHint(getDecoratedHint(hint)); } + } else { + mQueryTextView.setHint(getDecoratedHint("")); } } @@ -922,9 +964,13 @@ public class SearchView extends LinearLayout { CharSequence text = mQueryTextView.getText(); if (TextUtils.isEmpty(text)) { if (mIconifiedByDefault) { - // query field already empty, hide the keyboard and remove focus - clearFocus(); - setImeVisibility(false); + // If the app doesn't override the close behavior + if (mOnCloseListener == null || !mOnCloseListener.onClose()) { + // hide the keyboard and remove focus + clearFocus(); + // collapse the search field + updateViewsVisibility(true); + } } } else { mQueryTextView.setText(""); @@ -932,10 +978,6 @@ public class SearchView extends LinearLayout { setImeVisibility(true); } - if (mIconifiedByDefault && (mOnCloseListener == null || !mOnCloseListener.onClose())) { - updateViewsVisibility(mIconifiedByDefault); - setImeVisibility(false); - } } private void onSearchClicked() { @@ -975,6 +1017,28 @@ public class SearchView extends LinearLayout { updateFocusedState(mQueryTextView.hasFocus()); } + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + } + + private void adjustDropDownSizeAndPosition() { + if (mDropDownAnchor.getWidth() > 1) { + Resources res = getContext().getResources(); + int anchorPadding = mSearchPlate.getPaddingLeft(); + Rect dropDownPadding = new Rect(); + int iconOffset = mIconifiedByDefault + ? res.getDimensionPixelSize(R.dimen.dropdownitem_icon_width) + + res.getDimensionPixelSize(R.dimen.dropdownitem_text_padding_left) + : 0; + mQueryTextView.getDropDownBackground().getPadding(dropDownPadding); + mQueryTextView.setDropDownHorizontalOffset(-(dropDownPadding.left + iconOffset) + + anchorPadding); + mQueryTextView.setDropDownWidth(mDropDownAnchor.getWidth() + dropDownPadding.left + + dropDownPadding.right + iconOffset - (anchorPadding)); + } + } + private boolean onItemClicked(int position, int actionKey, String actionMsg) { if (mOnSuggestionListener == null || !mOnSuggestionListener.onSuggestionClick(position)) { @@ -1393,5 +1457,32 @@ public class SearchView extends LinearLayout { public boolean enoughToFilter() { return mThreshold <= 0 || super.enoughToFilter(); } + + @Override + public boolean onKeyPreIme(int keyCode, KeyEvent event) { + if (keyCode == KeyEvent.KEYCODE_BACK) { + // special case for the back key, we do not even try to send it + // to the drop down list but instead, consume it immediately + if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { + KeyEvent.DispatcherState state = getKeyDispatcherState(); + if (state != null) { + state.startTracking(event, this); + } + return true; + } else if (event.getAction() == KeyEvent.ACTION_UP) { + KeyEvent.DispatcherState state = getKeyDispatcherState(); + if (state != null) { + state.handleUpEvent(event); + } + if (event.isTracking() && !event.isCanceled()) { + mSearchView.clearFocus(); + mSearchView.setImeVisibility(false); + return true; + } + } + } + return super.onKeyPreIme(keyCode, event); + } + } } diff --git a/core/java/android/widget/SuggestionsAdapter.java b/core/java/android/widget/SuggestionsAdapter.java index 2cfc0169a46ea..9e32c9ab14bcc 100644 --- a/core/java/android/widget/SuggestionsAdapter.java +++ b/core/java/android/widget/SuggestionsAdapter.java @@ -16,8 +16,6 @@ package android.widget; -import com.android.internal.R; - import android.app.SearchDialog; import android.app.SearchManager; import android.app.SearchableInfo; @@ -47,6 +45,8 @@ import android.view.View; import android.view.ViewGroup; import android.view.View.OnClickListener; +import com.android.internal.R; + import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStream; @@ -88,8 +88,8 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene private int mIconName2Col = INVALID_INDEX; private int mFlagsCol = INVALID_INDEX; - private final Runnable mStartSpinnerRunnable; - private final Runnable mStopSpinnerRunnable; + // private final Runnable mStartSpinnerRunnable; + // private final Runnable mStopSpinnerRunnable; /** * The amount of time we delay in the filter when the user presses the delete key. @@ -113,17 +113,18 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene mOutsideDrawablesCache = outsideDrawablesCache; - mStartSpinnerRunnable = new Runnable() { - public void run() { - // mSearchView.setWorking(true); // TODO: - } - }; - mStopSpinnerRunnable = new Runnable() { - public void run() { - // mSearchView.setWorking(false); // TODO: - } - }; + // mStartSpinnerRunnable = new Runnable() { + // public void run() { + // // mSearchView.setWorking(true); // TODO: + // } + // }; + // + // mStopSpinnerRunnable = new Runnable() { + // public void run() { + // // mSearchView.setWorking(false); // TODO: + // } + // }; // delay 500ms when deleting getFilter().setDelayer(new Filter.Delayer() { @@ -341,10 +342,10 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene } if (views.mIcon1 != null) { - setViewDrawable(views.mIcon1, getIcon1(cursor)); + setViewDrawable(views.mIcon1, getIcon1(cursor), View.INVISIBLE); } if (views.mIcon2 != null) { - setViewDrawable(views.mIcon2, getIcon2(cursor)); + setViewDrawable(views.mIcon2, getIcon2(cursor), View.GONE); } if (mQueryRefinement == REFINE_ALL || (mQueryRefinement == REFINE_BY_ENTRY @@ -414,13 +415,13 @@ class SuggestionsAdapter extends ResourceCursorAdapter implements OnClickListene * Sets the drawable in an image view, makes sure the view is only visible if there * is a drawable. */ - private void setViewDrawable(ImageView v, Drawable drawable) { + private void setViewDrawable(ImageView v, Drawable drawable, int nullVisibility) { // Set the icon even if the drawable is null, since we need to clear any // previous icon. v.setImageDrawable(drawable); if (drawable == null) { - v.setVisibility(View.GONE); + v.setVisibility(nullVisibility); } else { v.setVisibility(View.VISIBLE); diff --git a/core/res/res/layout/search_bar.xml b/core/res/res/layout/search_bar.xml index 790ac6b09601b..f6b5b5372fe1d 100644 --- a/core/res/res/layout/search_bar.xml +++ b/core/res/res/layout/search_bar.xml @@ -66,7 +66,6 @@ android:layout_height="wrap_content" android:layout_weight="1" android:maxWidth="600dip" - android:iconifiedByDefault="false" android:layout_gravity="center_vertical" /> diff --git a/core/res/res/layout/search_dropdown_item_icons_2line.xml b/core/res/res/layout/search_dropdown_item_icons_2line.xml index 53906f94e0254..acef2cc71ef00 100644 --- a/core/res/res/layout/search_dropdown_item_icons_2line.xml +++ b/core/res/res/layout/search_dropdown_item_icons_2line.xml @@ -19,21 +19,21 @@ --> + android:visibility="invisible" /> @@ -30,7 +32,7 @@ android:id="@+id/search_badge" android:layout_width="wrap_content" android:layout_height="match_parent" - android:layout_gravity="center_vertical" + android:gravity="center_vertical" android:layout_marginBottom="2dip" android:drawablePadding="0dip" android:textAppearance="?android:attr/textAppearanceMedium" @@ -54,12 +56,21 @@ android:layout_height="wrap_content" android:layout_weight="1" android:layout_gravity="center_vertical" - android:layout_marginLeft="8dip" - android:layout_marginRight="8dip" android:layout_marginTop="4dip" android:layout_marginBottom="4dip" android:orientation="horizontal"> + + - - - + 16dip + + 6dip + + + 6dip + + + 48dip + diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index 5b5e7c350f388..d647467a59fc7 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -563,8 +563,8 @@