/*
* Copyright (C) 2013 Simon Vig Therkildsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.simonvt.cathode.widget;
import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.graphics.drawable.VectorDrawableCompat;
import android.support.v7.view.CollapsibleActionView;
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.view.KeyEvent;
import android.view.View;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import android.widget.LinearLayout;
import butterknife.BindView;
import butterknife.ButterKnife;
import net.simonvt.cathode.R;
public class SearchView extends LinearLayout implements CollapsibleActionView {
public interface SearchViewListener {
void onTextChanged(String newText);
void onSubmit(String query);
}
@BindView(R.id.search_input) EditText inputView;
@BindView(R.id.search_clear) View clearView;
private SearchViewListener listener;
private int maxWidth;
public SearchView(Context context) {
super(context);
init(context);
}
public SearchView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SearchView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
maxWidth = getResources().getDimensionPixelSize(R.dimen.searchViewMaxWidth);
}
@Override protected void onFinishInflate() {
super.onFinishInflate();
ButterKnife.bind(this);
inputView.setOnKeyListener(inputKeyListener);
inputView.addTextChangedListener(inputListener);
SpannableStringBuilder ssb = new SpannableStringBuilder(" ");
ssb.append(getResources().getString(R.string.action_search));
Drawable searchIcon =
VectorDrawableCompat.create(getResources(), R.drawable.ic_action_search_24dp, null);
int textSize = (int) (inputView.getTextSize() * 1.25);
searchIcon.setBounds(0, 0, textSize, textSize);
ssb.setSpan(new ImageSpan(searchIcon), 1, 2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
inputView.setHint(ssb);
clearView.setOnClickListener(clearListener);
clearView.setVisibility(inputView.getText().toString().isEmpty() ? INVISIBLE : VISIBLE);
}
private Runnable showIme = new Runnable() {
@Override public void run() {
InputMethodManager imm =
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.showSoftInput(inputView, 0);
}
}
};
@Override public void clearFocus() {
super.clearFocus();
inputView.clearFocus();
hideIme();
}
@Override public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
inputView.requestFocus();
showIme();
return true;
}
@Override public void onActionViewExpanded() {
inputView.requestFocus();
showIme();
}
@Override public void onActionViewCollapsed() {
hideIme();
}
private void showIme() {
post(showIme);
}
private void hideIme() {
removeCallbacks(showIme);
InputMethodManager imm =
(InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
}
public void setListener(SearchViewListener listener) {
this.listener = listener;
}
private OnClickListener clearListener = new OnClickListener() {
@Override public void onClick(View v) {
inputView.setText("");
}
};
private OnKeyListener inputKeyListener = new OnKeyListener() {
@Override public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {
if (listener != null) {
listener.onSubmit(inputView.getText().toString());
}
}
}
return false;
}
};
private TextWatcher inputListener = 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) {
if (listener != null) listener.onTextChanged(s.toString());
clearView.setVisibility(TextUtils.isEmpty(inputView.getText()) ? INVISIBLE : VISIBLE);
}
@Override public void afterTextChanged(Editable s) {
}
};
public void setQuery(CharSequence query) {
inputView.setText(query);
}
public CharSequence getQuery() {
return inputView.getText();
}
private int getPreferredWidth() {
return getContext().getResources()
.getDimensionPixelSize(
android.support.v7.appcompat.R.dimen.abc_search_view_preferred_width);
}
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
switch (widthMode) {
case MeasureSpec.AT_MOST:
// If there is an upper limit, don't exceed maximum width (explicit or implicit)
if (maxWidth > 0) {
width = Math.min(maxWidth, width);
} else {
width = Math.min(getPreferredWidth(), width);
}
break;
case MeasureSpec.EXACTLY:
// If an exact width is specified, still don't exceed any specified maximum width
if (maxWidth > 0) {
width = Math.min(maxWidth, width);
}
break;
case MeasureSpec.UNSPECIFIED:
// Use maximum width, if specified, else preferred width
width = maxWidth > 0 ? maxWidth : getPreferredWidth();
break;
}
widthMode = MeasureSpec.EXACTLY;
super.onMeasure(MeasureSpec.makeMeasureSpec(width, widthMode), heightMeasureSpec);
}
}