package com.lapism.searchview;
import android.annotation.TargetApi;
import android.app.Activity;
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.Rect;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.speech.RecognizerIntent;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.CardView;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.Filter;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.List;
public class SearchView extends FrameLayout implements Filter.FilterListener {
public static final int STYLE_CLASSIC = 0;
public static final int STYLE_COLOR = 1;
public static final int THEME_LIGHT = 0;
public static final int THEME_DARK = 1;
public static final int SPEECH_REQUEST_CODE = 0;
private int mStyle = 0;
private boolean mIsSearchOpen = false;
private boolean mClearingFocus;
private boolean allowVoiceSearch;
private final Context mContext;
private RecyclerView mSuggestionsRecyclerView;
private CardView mCardView;
private EditText mSearchEditText;
private ImageView mBackImageView;
private ImageView mVoiceImageView;
private ImageView mEmptyImageView;
private View mTintView;
private View mSeparatorView;
private SearchAdapter mSearchAdapter;
private OnQueryTextListener mOnQueryChangeListener;
private SearchViewListener mSearchViewListener;
private CharSequence mOldQueryText;
private CharSequence mUserQuery;
private SavedState mSavedState;
private View menuItem;
private boolean clearSearch;
public SearchView(Context context) {
this(context, null);
}
public SearchView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
initView();
initStyle(attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SearchView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mContext = context;
initView();
initStyle(attrs, defStyleAttr);
}
private void initView() {
LayoutInflater.from(mContext).inflate((R.layout.search_view), this, true);
setVisibility(View.GONE);
SearchLinearLayoutManager layoutManager = new SearchLinearLayoutManager(mContext, SearchLinearLayoutManager.VERTICAL, false);
layoutManager.clearChildSize();
layoutManager.setChildSize(getResources().getDimensionPixelSize(R.dimen.search_item_height));
mSuggestionsRecyclerView = (RecyclerView) findViewById(R.id.recyclerView);
mSuggestionsRecyclerView.setLayoutManager(layoutManager);
//mSuggestionsRecyclerView.setHasFixedSize(true);
mSuggestionsRecyclerView.setItemAnimator(new DefaultItemAnimator());
mSuggestionsRecyclerView.setVisibility(View.GONE);
mCardView = (CardView) findViewById(R.id.cardView);
mCardView.setVisibility(View.INVISIBLE);
mSearchEditText = (EditText) findViewById(R.id.editText_input);
mSearchEditText.setOnClickListener(mOnClickListener);
mBackImageView = (ImageView) findViewById(R.id.imageView_arrow_back);
mBackImageView.setOnClickListener(mOnClickListener);
mVoiceImageView = (ImageView) findViewById(R.id.imageView_mic);
mVoiceImageView.setOnClickListener(mOnClickListener);
mEmptyImageView = (ImageView) findViewById(R.id.imageView_clear);
mEmptyImageView.setOnClickListener(mOnClickListener);
mTintView = findViewById(R.id.view_transparent);
mTintView.setOnClickListener(mOnClickListener);
mSeparatorView = findViewById(R.id.view_separator);
mSeparatorView.setVisibility(View.GONE);
allowVoiceSearch = false;
showVoice(true);
initSearchView();
}
private boolean isVoiceAvailable() {
PackageManager pm = getContext().getPackageManager();
List<ResolveInfo> activities = pm.queryIntentActivities(
new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH), 0);
return (activities.size() != 0);
}
private void initStyle(AttributeSet attributeSet, int defStyleAttr) {
TypedArray attr = mContext.obtainStyledAttributes(attributeSet, R.styleable.SearchView, defStyleAttr, 0);
if (attr != null) {
if (attr.hasValue(R.styleable.SearchView_search_style)) {
setStyle(attr.getInt(R.styleable.SearchView_search_style, 0));
}
if (attr.hasValue(R.styleable.SearchView_search_theme)) {
setTheme(attr.getInt(R.styleable.SearchView_search_theme, 0));
}
if (attr.hasValue(R.styleable.SearchView_search_divider)) {
setDivider(attr.getBoolean(R.styleable.SearchView_search_divider, false));
}
attr.recycle();
}
}
private void onVoiceClicked() {
Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, "Speak now");
// intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH);
intent.putExtra(RecognizerIntent.EXTRA_MAX_RESULTS, 1);
if (mContext instanceof Activity) {
((Activity) mContext).startActivityForResult(intent, SPEECH_REQUEST_CODE);
}
}
private void showSuggestions() {
if (mSearchAdapter != null && mSearchAdapter.getItemCount() > 0 && mSuggestionsRecyclerView.getVisibility() == View.GONE) {
mSuggestionsRecyclerView.setVisibility(View.VISIBLE);
mSeparatorView.setVisibility(View.VISIBLE);
}
}
private void hideSuggestions() {
if (mSuggestionsRecyclerView.getVisibility() == View.VISIBLE) {
mSuggestionsRecyclerView.setVisibility(View.GONE);
mSeparatorView.setVisibility(View.GONE);
}
}
private void onSubmitQuery() {
CharSequence query = mSearchEditText.getText();
if (query != null && TextUtils.getTrimmedLength(query) > 0) {
if (mOnQueryChangeListener == null || !mOnQueryChangeListener.onQueryTextSubmit(query.toString())) {
mSearchEditText.setText(null);
}
}
}
private void startFilter(CharSequence s) {
if (mSearchAdapter != null) {
(mSearchAdapter).getFilter().filter(s, SearchView.this);
}
}
private final OnClickListener mOnClickListener = new OnClickListener() {
@Override
public void onClick(View v) {
if (v == mBackImageView) {
closeSearch(true);
} else if (v == mVoiceImageView) {
onVoiceClicked();
} else if (v == mEmptyImageView) {
mSearchEditText.setText(null);
} else if (v == mSearchEditText) {
showSuggestions();
} else if (v == mTintView) {
closeSearch(true);
}
}
};
private void showKeyboard() {
InputMethodManager imm = (InputMethodManager) mSearchEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mSearchEditText, 0);
}
private void hideKeyboard() {
InputMethodManager imm = (InputMethodManager) mSearchEditText.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(mSearchEditText.getWindowToken(), 0);
}
private void onTextChanged(CharSequence newText) {
CharSequence text = mSearchEditText.getText();
mUserQuery = text;
boolean hasText = !TextUtils.isEmpty(text);
if (hasText) {
mEmptyImageView.setVisibility(View.VISIBLE);
showVoice(false);
} else {
mEmptyImageView.setVisibility(View.GONE);
showVoice(true);
}
if (mOnQueryChangeListener != null && !TextUtils.equals(newText, mOldQueryText)
&& isSearchOpen() && clearSearch) {
mOnQueryChangeListener.onQueryTextChange(newText.toString());
}
mOldQueryText = newText.toString();
}
private void showVoice(boolean show) {
if (show && isVoiceAvailable() && allowVoiceSearch) {
mVoiceImageView.setVisibility(View.VISIBLE);
} else {
mVoiceImageView.setVisibility(View.GONE);
}
}
/* Need update ********************************************************************************/
private void initSearchView() {
mSearchEditText.setOnEditorActionListener(new TextView.OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
onSubmitQuery();
return true;
}
});
mSearchEditText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
mUserQuery = s;
startFilter(s);
SearchView.this.onTextChanged(s);
}
@Override
public void afterTextChanged(Editable s) {
}
});
mSearchEditText.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus) {
showKeyboard();
showSuggestions();
} else {
hideKeyboard();
hideSuggestions();
}
}
});
}
/* Need update ********************************************************************************/
public void setVoiceSearch(boolean voiceSearch) {
allowVoiceSearch = voiceSearch;
}
public void setHint(CharSequence hint) {
mSearchEditText.setHint(hint);
}
public void setHint(int hint) {
mSearchEditText.setHint(hint);
}
public void setStyle(int style) {
if (style == STYLE_CLASSIC) {
mBackImageView.setImageResource(R.drawable.search_ic_arrow_back_black_24dp);
mVoiceImageView.setImageResource(R.drawable.search_ic_mic_black_24dp);
mEmptyImageView.setImageResource(R.drawable.search_ic_clear_black_24dp);
}
if (style == STYLE_COLOR) {
mBackImageView.setImageResource(R.drawable.search_ic_arrow_back_color_24dp);
mVoiceImageView.setImageResource(R.drawable.search_ic_mic_color_24dp);
mEmptyImageView.setImageResource(R.drawable.search_ic_clear_color_24dp);
}
mStyle = style;
}
public void setTheme(int theme) {
if (theme == THEME_LIGHT) {
if (mStyle == STYLE_CLASSIC) {
mBackImageView.setColorFilter(ContextCompat.getColor(mContext, R.color.search_light_icon));
mVoiceImageView.setColorFilter(ContextCompat.getColor(mContext, R.color.search_light_icon));
mEmptyImageView.setColorFilter(ContextCompat.getColor(mContext, R.color.search_light_icon));
}
mSeparatorView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.search_light_separator));
mSuggestionsRecyclerView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.search_light_background));
mCardView.setCardBackgroundColor(ContextCompat.getColor(mContext, R.color.search_light_background));
mSearchEditText.setBackgroundColor(ContextCompat.getColor(mContext, R.color.search_light_background));
mSearchEditText.setTextColor(ContextCompat.getColor(mContext, R.color.search_light_text));
mSearchEditText.setHintTextColor(ContextCompat.getColor(mContext, R.color.search_light_text_hint));
}
if (theme == THEME_DARK) {
if (mStyle == STYLE_CLASSIC) {
mBackImageView.setColorFilter(ContextCompat.getColor(mContext, R.color.search_dark_icon));
mVoiceImageView.setColorFilter(ContextCompat.getColor(mContext, R.color.search_dark_icon));
mEmptyImageView.setColorFilter(ContextCompat.getColor(mContext, R.color.search_dark_icon));
}
mSeparatorView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.search_dark_separator));
mSuggestionsRecyclerView.setBackgroundColor(ContextCompat.getColor(mContext, R.color.search_dark_background));
mCardView.setCardBackgroundColor(ContextCompat.getColor(mContext, R.color.search_dark_background));
mSearchEditText.setBackgroundColor(ContextCompat.getColor(mContext, R.color.search_dark_background));
mSearchEditText.setTextColor(ContextCompat.getColor(mContext, R.color.search_dark_text));
mSearchEditText.setHintTextColor(ContextCompat.getColor(mContext, R.color.search_dark_text_hint));
}
}
public void setDivider(boolean divider) {
if (divider) {
mSuggestionsRecyclerView.addItemDecoration(new SearchViewDivider(mContext, null));
}
}
public boolean isSearchOpen() {
return mIsSearchOpen;
}
public void setAdapter(SearchAdapter adapter) {
mSearchAdapter = adapter;
mSuggestionsRecyclerView.setAdapter(adapter);
startFilter(mSearchEditText.getText());
}
public interface OnQueryTextListener {
boolean onQueryTextSubmit(String query);
boolean onQueryTextChange(String newText);
}
public interface SearchViewListener {
void onSearchViewShown();
void onSearchViewClosed();
}
public void setOnQueryTextListener(OnQueryTextListener listener) {
mOnQueryChangeListener = listener;
}
public void setOnSearchViewListener(SearchViewListener listener) {
mSearchViewListener = listener;
}
public void showSearch(View item, boolean animate) {
this.clearSearch = true;
menuItem = item;
if (isSearchOpen()) {
return;
}
mSearchEditText.setText(null);
mSearchEditText.requestFocus();
setVisibility(View.VISIBLE);
if (animate) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
revealInAnimation();
} else {
SearchAnimator.fadeInAnimation(mCardView, SearchAnimator.ANIMATION_DURATION);
}
} else {
mCardView.setVisibility(VISIBLE);
}
if (mSearchViewListener != null) {
mSearchViewListener.onSearchViewShown();
}
mIsSearchOpen = true;
}
public void closeSearch(boolean animate) {
this.clearSearch = false;
if (!isSearchOpen()) {
return;
}
mSearchEditText.setText(null);
mSearchEditText.clearFocus();
clearFocus();
if (animate) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
final int endCy = mSearchEditText.getHeight() / 2;
SearchAnimator.revealOutAnimation(mContext, menuItem, mCardView, endCy, SearchAnimator.ANIMATION_DURATION);
} else {
SearchAnimator.fadeOutAnimation(mCardView, SearchAnimator.ANIMATION_DURATION);
}
postDelayed(new Runnable() {
@Override
public void run() {
setVisibility(View.GONE);
if (mSearchViewListener != null) {
mSearchViewListener.onSearchViewClosed();
}
}
}, SearchAnimator.ANIMATION_DURATION);
} else {
setVisibility(View.GONE);
if (mSearchViewListener != null) {
mSearchViewListener.onSearchViewClosed();
}
}
mIsSearchOpen = false;
}
public void setQuery(CharSequence query, boolean submit) {
mSearchEditText.setText(query);
if (query != null) {
mSearchEditText.setSelection(mSearchEditText.length());
mUserQuery = query;
}
if (submit && !TextUtils.isEmpty(query)) {
onSubmitQuery();
}
}
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
return !mClearingFocus && isFocusable() && mSearchEditText.requestFocus(direction, previouslyFocusedRect);
}
@Override
public void clearFocus() {
mClearingFocus = true;
hideKeyboard();
super.clearFocus();
mSearchEditText.clearFocus();
mClearingFocus = false;
}
@Override
public Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
mSavedState = new SavedState(superState);
mSavedState.query = mUserQuery != null ? mUserQuery.toString() : null;
mSavedState.isSearchOpen = this.mIsSearchOpen;
return mSavedState;
}
@Override
public void onFilterComplete(int count) {
if (count > 0) {
showSuggestions();
} else {
hideSuggestions();
}
}
@Override
public void onRestoreInstanceState(Parcelable state) {
if (!(state instanceof SavedState)) {
super.onRestoreInstanceState(state);
return;
}
mSavedState = (SavedState) state;
if (mSavedState.isSearchOpen) {
showSearch(menuItem, true);
setQuery(mSavedState.query, false);
}
super.onRestoreInstanceState(mSavedState.getSuperState());
}
private static class SavedState extends BaseSavedState {
public static final Creator<SavedState> CREATOR =
new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
String query;
boolean isSearchOpen;
public SavedState(Parcelable superState) {
super(superState);
}
private SavedState(Parcel in) {
super(in);
this.query = in.readString();
this.isSearchOpen = in.readInt() == 1;
}
@Override
public void writeToParcel(Parcel out, int flags) {
super.writeToParcel(out, flags);
out.writeString(query);
out.writeInt(isSearchOpen ? 1 : 0);
}
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void revealInAnimation() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
mCardView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
@Override
public void onGlobalLayout() {
mCardView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
final int startCy = mSearchEditText.getHeight() / 2;
SearchAnimator.revealInAnimation(mContext, menuItem, mCardView, startCy, SearchAnimator.ANIMATION_DURATION);
}
});
}
}
@Deprecated
public void setMenuItem(final MenuItem menuItem) {
menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
showSearch(SearchView.this.menuItem, true);
return true;
}
});
}
/*private void setUpLayoutTransition() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
LinearLayout searchRoot = (LinearLayout) findViewById(R.id.search_layout_item);
LayoutTransition layoutTransition = new LayoutTransition();
layoutTransition.setDuration(SearchAnimator.ANIMATION_DURATION);
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN) {
// layoutTransition.enableTransitionType(LayoutTransition.CHANGING);
layoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
layoutTransition.setStartDelay(LayoutTransition.CHANGING, 0);
}
layoutTransition.setStartDelay(LayoutTransition.CHANGE_DISAPPEARING, 0);
mCardView.setLayoutTransition(layoutTransition);
}
}*/
}