Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit b4569fb1 authored by Amith Yamasani's avatar Amith Yamasani
Browse files

SearchView behavioral and visual changes

Align text suggestions and search string.
Close button only clears the field.
Back button clears focus, hides dropdown and keyboard.
Tweaked SearchDialog's use of SearchView to follow above patterns.
Fixed other little bugs.

Change-Id: I9d34c2ebe2b1b2ca887220894c19a26809db85f6
parent 3ad4d3ce
Loading
Loading
Loading
Loading
+1 −25
Original line number Diff line number Diff line
@@ -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.
         */
+6 −0
Original line number Diff line number Diff line
@@ -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"
+104 −13
Original line number Diff line number Diff line
@@ -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
                // If the app doesn't override the close behavior
                if (mOnCloseListener == null || !mOnCloseListener.onClose()) {
                    // hide the keyboard and remove focus
                    clearFocus();
                setImeVisibility(false);
                    // 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);
        }

    }
}
+19 −18
Original line number Diff line number Diff line
@@ -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);

+0 −1
Original line number Diff line number Diff line
@@ -66,7 +66,6 @@
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:maxWidth="600dip"
            android:iconifiedByDefault="false"
            android:layout_gravity="center_vertical"
            />

Loading