package com.jdroid.android.recycler; import android.os.Bundle; import android.text.Editable; import android.text.TextWatcher; import android.view.View; import android.view.View.OnClickListener; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import com.jdroid.android.R; import com.jdroid.android.exception.DialogErrorDisplayer; import com.jdroid.android.exception.ErrorDisplayer; import com.jdroid.android.fragment.FragmentHelper.UseCaseTrigger; import com.jdroid.android.listener.OnEnterKeyListener; import com.jdroid.android.usecase.SearchUseCase; import com.jdroid.android.utils.AppUtils; import com.jdroid.android.utils.ToastUtils; import com.jdroid.java.exception.AbstractException; import com.jdroid.java.utils.StringUtils; /** * Base search Fragment. It has a search text and a list with the results. */ public abstract class AbstractSearchPaginatedRecyclerFragment extends AbstractPaginatedRecyclerFragment { private int threshold = 1; private EditText searchText; private View loading; @Override public Integer getContentFragmentLayout() { return R.layout.jdroid_abstract_search_fragment; } @Override public void onViewCreated(View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); searchText = findView(R.id.searchText); searchText.setHint(getSearchEditTextHintResId()); searchText.setOnFocusChangeListener(new View.OnFocusChangeListener() { @Override public void onFocusChange(View v, boolean hasFocus) { if (hasFocus) { AppUtils.showSoftInput(getActivity()); } else { AppUtils.hideSoftInput(v); } } }); searchText.requestFocus(); if (isInstantSearchEnabled()) { searchText.addTextChangedListener(getTextWatcher()); searchText.setImeOptions(EditorInfo.IME_ACTION_DONE); } else { searchText.setOnKeyListener(new OnEnterKeyListener(false) { @Override public void onRun(View view) { search(); } }); } View searchButton = findView(R.id.searchButton); if (isInstantSearchEnabled()) { if (searchButton != null) { searchButton.setVisibility(View.GONE); } } else { searchButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { search(); } }); } View cancelButton = findView(R.id.cancelButton); if (displayCancelButton()) { cancelButton.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { doCancel(); } }); } else { cancelButton.setVisibility(View.GONE); } loading = findView(R.id.loading); } @Override public void onPause() { super.onPause(); AppUtils.hideSoftInput(getView()); } public Boolean isInstantSearchEnabled() { return true; } /** * @return <code>true</code> if the amount of text in the field meets or exceeds the {@link #getThreshold} * requirement. You can override this to impose a different standard for when filtering will be triggered. */ public boolean enoughToFilter() { return searchText.getText().length() >= threshold; } protected TextWatcher getTextWatcher() { return 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) { } @Override public void afterTextChanged(Editable prefix) { AbstractSearchPaginatedRecyclerFragment.this.afterTextChanged(prefix.toString()); } }; } protected void afterTextChanged(String text) { if (enoughToFilter()) { search(); } else { if (getAdapter() != null) { getAdapter().clear(); } if (emptyViewContainer != null) { emptyViewContainer.setVisibility(View.GONE); } } } protected int getNoResultsResId() { return R.string.jdroid_noResultsSearch; } protected int getSearchEditTextHintResId() { return R.string.jdroid_typeHere; } @Override protected UseCaseTrigger getUseCaseTrigger() { return UseCaseTrigger.MANUAL; } private void search() { String searchValue = searchText.getText().toString(); if (StringUtils.isNotEmpty(searchValue) || !isSearchValueRequired()) { doSearch(searchValue); if (!isInstantSearchEnabled()) { searchText.clearFocus(); } } else { ToastUtils.showToast(R.string.jdroid_requiredSearchTerm); } } protected void doCancel() { searchText.setText(null); getSearchUseCase().setSearchValue(null); getSearchUseCase().reset(); dismissLoading(); if (getAdapter() != null) { getAdapter().clear(); } if (emptyViewContainer != null) { emptyViewContainer.setVisibility(View.GONE); } } protected void doSearch(String searchValue) { getSearchUseCase().setSearchValue(searchValue); getSearchUseCase().reset(); if (!isInstantSearchEnabled() && getAdapter() != null) { getAdapter().clear(); } executeUseCase(getSearchUseCase()); } @Override public void showLoading() { executeOnUIThread(new Runnable() { @Override public void run() { if (loading != null) { loading.setVisibility(View.VISIBLE); } } }); } @Override public ErrorDisplayer createErrorDisplayer(AbstractException abstractException) { DialogErrorDisplayer.markAsNotGoBackOnError(abstractException); return super.createErrorDisplayer(abstractException); } @Override public void dismissLoading() { executeOnUIThread(new Runnable() { @Override public void run() { if (loading != null) { loading.setVisibility(View.INVISIBLE); } } }); } protected boolean displayCancelButton() { return true; } /** * @return Whether a search can be performed without a search value or not. */ public boolean isSearchValueRequired() { return false; } /** * @return the threshold */ public int getThreshold() { return threshold; } /** * @param threshold the threshold to set */ public void setThreshold(int threshold) { if (threshold <= 0) { threshold = 1; } this.threshold = threshold; } /** * @return the searchText */ public EditText getSearchText() { return searchText; } @Override protected SearchUseCase<Object> createPaginatedUseCase() { return null; } private SearchUseCase<Object> getSearchUseCase() { return (SearchUseCase<Object>)paginatedUseCase; } }