From c8e8a0418826972f25f66f36d7aefef37efde2e1 Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Tue, 22 Jul 2014 17:02:20 -0700 Subject: [PATCH] Bug 1041026 - Move search bar logic into custom view. r=eedens --- .../java/org/mozilla/search/MainActivity.java | 5 + .../autocomplete/ClearableEditText.java | 123 +++++++++++++++ .../search/autocomplete/SearchFragment.java | 144 ++++++------------ .../search/res/layout/clearable_edit_text.xml | 28 ++++ .../res/layout/search_auto_complete.xml | 29 +--- .../search/search_activity_sources.mozbuild | 1 + 6 files changed, 203 insertions(+), 127 deletions(-) create mode 100644 mobile/android/search/java/org/mozilla/search/autocomplete/ClearableEditText.java create mode 100644 mobile/android/search/res/layout/clearable_edit_text.xml diff --git a/mobile/android/search/java/org/mozilla/search/MainActivity.java b/mobile/android/search/java/org/mozilla/search/MainActivity.java index c08cae13e35..3c3e5bcd58b 100644 --- a/mobile/android/search/java/org/mozilla/search/MainActivity.java +++ b/mobile/android/search/java/org/mozilla/search/MainActivity.java @@ -13,6 +13,7 @@ import android.view.View; import org.mozilla.gecko.db.BrowserContract.SearchHistory; import org.mozilla.search.autocomplete.AcceptsSearchQuery; +import org.mozilla.search.autocomplete.SearchFragment; /** * The main entrance for the Android search intent. @@ -58,8 +59,12 @@ public class MainActivity extends FragmentActivity implements AcceptsSearchQuery public void onSearch(String query) { startPostsearch(); storeQuery(query); + ((PostSearchFragment) getSupportFragmentManager().findFragmentById(R.id.postsearch)) .setUrl("https://search.yahoo.com/search?p=" + Uri.encode(query)); + + ((SearchFragment) getSupportFragmentManager().findFragmentById(R.id.search_fragment)) + .setSearchTerm(query); } private void startPresearch() { diff --git a/mobile/android/search/java/org/mozilla/search/autocomplete/ClearableEditText.java b/mobile/android/search/java/org/mozilla/search/autocomplete/ClearableEditText.java new file mode 100644 index 00000000000..dbe958aa69e --- /dev/null +++ b/mobile/android/search/java/org/mozilla/search/autocomplete/ClearableEditText.java @@ -0,0 +1,123 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.search.autocomplete; + +import android.content.Context; +import android.text.Editable; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.view.KeyEvent; +import android.view.LayoutInflater; +import android.view.MotionEvent; +import android.view.View; +import android.view.inputmethod.EditorInfo; +import android.view.inputmethod.InputMethodManager; +import android.widget.Button; +import android.widget.EditText; +import android.widget.FrameLayout; +import android.widget.TextView; + +import org.mozilla.search.R; + +public class ClearableEditText extends FrameLayout { + + private EditText editText; + private Button clearButton; + private InputMethodManager inputMethodManager; + + private TextListener listener; + + private boolean active; + + public interface TextListener { + public void onChange(String text); + public void onSubmit(String text); + } + + public ClearableEditText(Context context, AttributeSet attrs) { + super(context, attrs); + + LayoutInflater.from(context).inflate(R.layout.clearable_edit_text, this); + + editText = (EditText) findViewById(R.id.edit_text); + editText.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) { + } + + @Override + public void afterTextChanged(Editable s) { + if (listener != null) { + listener.onChange(s.toString()); + } + } + }); + + // Attach a listener for the "search" key on the keyboard. + editText.setOnEditorActionListener(new TextView.OnEditorActionListener() { + @Override + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { + if (listener != null && actionId == EditorInfo.IME_ACTION_SEARCH) { + listener.onSubmit(v.getText().toString()); + return true; + } + return false; + } + }); + + clearButton = (Button) findViewById(R.id.clear_button); + clearButton.setOnClickListener(new View.OnClickListener(){ + @Override + public void onClick(View v) { + editText.setText(""); + } + }); + + inputMethodManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); + } + + public void setText(String text) { + editText.setText(text); + + // Move cursor to end of search input. + editText.setSelection(text.length()); + } + + public void setActive(boolean active) { + if (this.active == active) { + return; + } + this.active = active; + + clearButton.setVisibility(active ? View.VISIBLE : View.GONE); + + editText.setFocusable(active); + editText.setFocusableInTouchMode(active); + + if (active) { + editText.requestFocus(); + inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); + } else { + editText.clearFocus(); + inputMethodManager.hideSoftInputFromWindow(editText.getWindowToken(), 0); + } + } + + public void setTextListener(TextListener listener) { + this.listener = listener; + } + + @Override + public boolean onInterceptTouchEvent(MotionEvent e) { + // When the view is active, pass touch events to child views. + // Otherwise, intercept touch events to allow click listeners on the view to + // fire no matter where the user clicks. + return !active; + } +} diff --git a/mobile/android/search/java/org/mozilla/search/autocomplete/SearchFragment.java b/mobile/android/search/java/org/mozilla/search/autocomplete/SearchFragment.java index d1b921b32ab..b3f82c16089 100644 --- a/mobile/android/search/java/org/mozilla/search/autocomplete/SearchFragment.java +++ b/mobile/android/search/java/org/mozilla/search/autocomplete/SearchFragment.java @@ -4,7 +4,6 @@ package org.mozilla.search.autocomplete; - import android.app.Activity; import android.content.Context; import android.os.Bundle; @@ -12,21 +11,13 @@ import android.support.v4.app.Fragment; import android.support.v4.app.LoaderManager; import android.support.v4.content.AsyncTaskLoader; import android.support.v4.content.Loader; -import android.text.Editable; import android.text.SpannableString; -import android.text.TextWatcher; import android.text.style.ForegroundColorSpan; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; -import android.view.inputmethod.InputMethodManager; import android.widget.AdapterView; -import android.widget.Button; -import android.widget.EditText; import android.widget.ListView; -import android.widget.TextView; import org.mozilla.search.R; @@ -39,8 +30,7 @@ import java.util.List; *

* TODO: Add more search providers (other than the dictionary) */ -public class SearchFragment extends Fragment - implements TextView.OnEditorActionListener, AcceptsJumpTaps { +public class SearchFragment extends Fragment implements AcceptsJumpTaps { private static final int LOADER_ID_SUGGESTION = 0; private static final String KEY_SEARCH_TERM = "search_term"; @@ -54,16 +44,14 @@ public class SearchFragment extends Fragment // Color of search term match in search suggestion private static final int SUGGESTION_HIGHLIGHT_COLOR = 0xFF999999; + private AcceptsSearchQuery searchListener; private SuggestClient suggestClient; private SuggestionLoaderCallbacks suggestionLoaderCallbacks; - private InputMethodManager inputMethodManager; private AutoCompleteAdapter autoCompleteAdapter; private View mainView; - private View searchBar; - private EditText editText; - private Button clearButton; + private ClearableEditText editText; private ListView suggestionDropdown; private State state = State.WAITING; @@ -81,6 +69,12 @@ public class SearchFragment extends Fragment public void onAttach(Activity activity) { super.onAttach(activity); + if (activity instanceof AcceptsSearchQuery) { + searchListener = (AcceptsSearchQuery) activity; + } else { + throw new ClassCastException(activity.toString() + " must implement AcceptsSearchQuery."); + } + // TODO: Don't hard-code this template string (bug 1039758) final String template = "https://search.yahoo.com/sugg/ff?" + "output=fxjson&appid=ffm&command=__searchTerms__&nresults=" + SUGGESTION_MAX; @@ -88,7 +82,6 @@ public class SearchFragment extends Fragment suggestClient = new SuggestClient(activity, template, SUGGESTION_TIMEOUT, SUGGESTION_MAX); suggestionLoaderCallbacks = new SuggestionLoaderCallbacks(); - inputMethodManager = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); autoCompleteAdapter = new AutoCompleteAdapter(activity, this); } @@ -96,9 +89,9 @@ public class SearchFragment extends Fragment public void onDetach() { super.onDetach(); + searchListener = null; suggestClient = null; suggestionLoaderCallbacks = null; - inputMethodManager = null; autoCompleteAdapter = null; } @@ -115,42 +108,28 @@ public class SearchFragment extends Fragment } }); - searchBar = mainView.findViewById(R.id.search_bar); - editText = (EditText) mainView.findViewById(R.id.search_bar_edit_text); - - final View.OnClickListener transitionToRunningListener = new View.OnClickListener() { + editText = (ClearableEditText) mainView.findViewById(R.id.auto_complete_edit_text); + editText.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { transitionToRunning(); } - }; - searchBar.setOnClickListener(transitionToRunningListener); - editText.setOnClickListener(transitionToRunningListener); - - // Attach a listener for the "search" key on the keyboard. - editText.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) { - } - - @Override - public void afterTextChanged(Editable s) { - final Bundle args = new Bundle(); - args.putString(KEY_SEARCH_TERM, s.toString()); - getLoaderManager().restartLoader(LOADER_ID_SUGGESTION, args, suggestionLoaderCallbacks); - } }); - editText.setOnEditorActionListener(this); - clearButton = (Button) mainView.findViewById(R.id.search_bar_clear_button); - clearButton.setOnClickListener(new View.OnClickListener(){ + editText.setTextListener(new ClearableEditText.TextListener() { @Override - public void onClick(View v) { - editText.setText(""); + public void onChange(String text) { + if (state == State.RUNNING) { + final Bundle args = new Bundle(); + args.putString(KEY_SEARCH_TERM, text); + getLoaderManager().restartLoader(LOADER_ID_SUGGESTION, args, suggestionLoaderCallbacks); + } + } + + @Override + public void onSubmit(String text) { + transitionToWaiting(); + searchListener.onSearch(text); } }); @@ -162,7 +141,9 @@ public class SearchFragment extends Fragment @Override public void onItemClick(AdapterView parent, View view, int position, long id) { final Suggestion suggestion = (Suggestion) suggestionDropdown.getItemAtPosition(position); - startSearch(suggestion.value); + + transitionToWaiting(); + searchListener.onSearch(suggestion.value); } }); @@ -173,9 +154,7 @@ public class SearchFragment extends Fragment public void onDestroyView() { super.onDestroyView(); - searchBar = null; editText = null; - clearButton = null; if (null != suggestionDropdown) { suggestionDropdown.setOnItemClickListener(null); @@ -184,78 +163,41 @@ public class SearchFragment extends Fragment } } - /** - * Handler for the "search" button on the keyboard. - */ @Override - public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { - if (actionId == EditorInfo.IME_ACTION_SEARCH) { - startSearch(v.getText().toString()); - return true; - } - return false; + public void onJumpTap(String suggestion) { + setSearchTerm(suggestion); } /** - * Send a search intent and put the widget into waiting. + * Sets the search term in the search bar. If the SearchFragment is + * in State.RUNNING, this will also update the search suggestions. + * + * @param searchTerm */ - private void startSearch(String queryString) { - if (getActivity() instanceof AcceptsSearchQuery) { - editText.setText(queryString); - editText.setSelection(queryString.length()); - transitionToWaiting(); - ((AcceptsSearchQuery) getActivity()).onSearch(queryString); - } else { - throw new RuntimeException("Parent activity does not implement AcceptsSearchQuery."); - } + public void setSearchTerm(String searchTerm) { + editText.setText(searchTerm); } private void transitionToWaiting() { if (state == State.WAITING) { return; } - - setEditTextFocusable(false); - mainView.setClickable(false); - - suggestionDropdown.setVisibility(View.GONE); - clearButton.setVisibility(View.GONE); - state = State.WAITING; + + mainView.setClickable(false); + editText.setActive(false); + suggestionDropdown.setVisibility(View.GONE); } private void transitionToRunning() { if (state == State.RUNNING) { return; } - - setEditTextFocusable(true); - mainView.setClickable(true); - - suggestionDropdown.setVisibility(View.VISIBLE); - clearButton.setVisibility(View.VISIBLE); - state = State.RUNNING; - } - private void setEditTextFocusable(boolean focusable) { - editText.setFocusable(focusable); - editText.setFocusableInTouchMode(focusable); - - if (focusable) { - editText.requestFocus(); - inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); - } else { - editText.clearFocus(); - inputMethodManager.hideSoftInputFromWindow(editText.getWindowToken(), 0); - } - } - - @Override - public void onJumpTap(String suggestion) { - editText.setText(suggestion); - // Move cursor to end of search input. - editText.setSelection(suggestion.length()); + mainView.setClickable(true); + editText.setActive(true); + suggestionDropdown.setVisibility(View.VISIBLE); } public static class Suggestion { diff --git a/mobile/android/search/res/layout/clearable_edit_text.xml b/mobile/android/search/res/layout/clearable_edit_text.xml new file mode 100644 index 00000000000..0cdc1285732 --- /dev/null +++ b/mobile/android/search/res/layout/clearable_edit_text.xml @@ -0,0 +1,28 @@ + + + + + + +