Bug 1022102 - Polish search bar interaction and styling. r=eedens

This commit is contained in:
Margaret Leibovic 2014-07-18 16:49:32 -07:00
parent d2ee603161
commit 325dbd5865
16 changed files with 140 additions and 153 deletions

View File

@ -37,9 +37,6 @@ public class PreSearchFragment extends ListFragment {
@Override @Override
public void onActivityCreated(Bundle savedInstanceState) { public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState); super.onActivityCreated(savedInstanceState);
View headerView = getLayoutInflater(savedInstanceState)
.inflate(R.layout.search_stream_header, getListView(), false);
getListView().addHeaderView(headerView, null, false);
if (null == adapter) { if (null == adapter) {
adapter = new ArrayAdapter<PreloadAgent.TmpItem>(getActivity(), R.layout.search_card, adapter = new ArrayAdapter<PreloadAgent.TmpItem>(getActivity(), R.layout.search_card,
R.id.card_title, PreloadAgent.ITEMS) { R.id.card_title, PreloadAgent.ITEMS) {

View File

@ -5,6 +5,7 @@
package org.mozilla.search.autocomplete; package org.mozilla.search.autocomplete;
import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.Fragment; import android.support.v4.app.Fragment;
@ -22,7 +23,6 @@ import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.ListView; import android.widget.ListView;
import android.widget.TextView; import android.widget.TextView;
@ -48,20 +48,21 @@ public class SearchFragment extends Fragment
// Maximum number of results returned by the suggestion client // Maximum number of results returned by the suggestion client
private static final int SUGGESTION_MAX = 5; private static final int SUGGESTION_MAX = 5;
private View mainView;
private FrameLayout backdropFrame;
private EditText searchBar;
private ListView suggestionDropdown;
private InputMethodManager inputMethodManager;
private AutoCompleteAdapter autoCompleteAdapter;
private SuggestClient suggestClient; private SuggestClient suggestClient;
private SuggestionLoaderCallbacks suggestionLoaderCallbacks; private SuggestionLoaderCallbacks suggestionLoaderCallbacks;
private State state; private InputMethodManager inputMethodManager;
private AutoCompleteAdapter autoCompleteAdapter;
private enum State { private View mainView;
private View searchBar;
private EditText editText;
private Button clearButton;
private ListView suggestionDropdown;
private State state = State.WAITING;
private static enum State {
WAITING, // The user is doing something else in the app. WAITING, // The user is doing something else in the app.
RUNNING // The user is in search mode. RUNNING // The user is in search mode.
} }
@ -70,20 +71,58 @@ public class SearchFragment extends Fragment
// Required empty public constructor // Required empty public constructor
} }
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// 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;
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);
}
@Override
public void onDetach() {
super.onDetach();
suggestClient = null;
suggestionLoaderCallbacks = null;
inputMethodManager = null;
autoCompleteAdapter = null;
}
@Override @Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
mainView = inflater.inflate(R.layout.search_auto_complete, container, false); mainView = inflater.inflate(R.layout.search_auto_complete, container, false);
backdropFrame = (FrameLayout) mainView.findViewById(R.id.auto_complete_backdrop);
searchBar = (EditText) mainView.findViewById(R.id.auto_complete_search_bar);
suggestionDropdown = (ListView) mainView.findViewById(R.id.auto_complete_dropdown);
inputMethodManager = // Intercept clicks on the main view to deactivate the search bar.
(InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE); mainView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
transitionToWaiting();
}
});
searchBar = mainView.findViewById(R.id.search_bar);
editText = (EditText) mainView.findViewById(R.id.search_bar_edit_text);
final View.OnClickListener transitionToRunningListener = 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. // Attach a listener for the "search" key on the keyboard.
searchBar.addTextChangedListener(new TextWatcher() { editText.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) { public void beforeTextChanged(CharSequence s, int start, int count, int after) {
} }
@ -99,66 +138,44 @@ public class SearchFragment extends Fragment
getLoaderManager().restartLoader(LOADER_ID_SUGGESTION, args, suggestionLoaderCallbacks); getLoaderManager().restartLoader(LOADER_ID_SUGGESTION, args, suggestionLoaderCallbacks);
} }
}); });
searchBar.setOnEditorActionListener(this); editText.setOnEditorActionListener(this);
searchBar.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (v.hasFocus()) {
return;
}
transitionToRunning();
}
});
final Button clearButton = (Button) mainView.findViewById(R.id.clear_button); clearButton = (Button) mainView.findViewById(R.id.search_bar_clear_button);
clearButton.setOnClickListener(new View.OnClickListener(){ clearButton.setOnClickListener(new View.OnClickListener(){
@Override @Override
public void onClick(View v) { public void onClick(View v) {
searchBar.setText(""); editText.setText("");
} }
}); });
backdropFrame.setOnClickListener(new BackdropClickListener()); suggestionDropdown = (ListView) mainView.findViewById(R.id.auto_complete_dropdown);
autoCompleteAdapter = new AutoCompleteAdapter(getActivity(), this);
suggestionDropdown.setAdapter(autoCompleteAdapter); suggestionDropdown.setAdapter(autoCompleteAdapter);
// This will hide the autocomplete box and background frame.
transitionToWaiting();
// Attach listener for tapping on a suggestion. // Attach listener for tapping on a suggestion.
suggestionDropdown.setOnItemClickListener(new AdapterView.OnItemClickListener() { suggestionDropdown.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override @Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) { public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
String query = (String) suggestionDropdown.getItemAtPosition(position); final String query = (String) suggestionDropdown.getItemAtPosition(position);
startSearch(query); startSearch(query);
} }
}); });
// 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;
suggestClient = new SuggestClient(getActivity(), template, SUGGESTION_TIMEOUT, SUGGESTION_MAX);
suggestionLoaderCallbacks = new SuggestionLoaderCallbacks();
return mainView; return mainView;
} }
@Override @Override
public void onDestroyView() { public void onDestroyView() {
super.onDestroyView(); super.onDestroyView();
inputMethodManager = null;
mainView = null;
searchBar = null; searchBar = null;
editText = null;
clearButton = null;
if (null != suggestionDropdown) { if (null != suggestionDropdown) {
suggestionDropdown.setOnItemClickListener(null); suggestionDropdown.setOnItemClickListener(null);
suggestionDropdown.setAdapter(null); suggestionDropdown.setAdapter(null);
suggestionDropdown = null; suggestionDropdown = null;
} }
autoCompleteAdapter = null;
suggestClient = null;
suggestionLoaderCallbacks = null;
} }
/** /**
@ -178,8 +195,8 @@ public class SearchFragment extends Fragment
*/ */
private void startSearch(String queryString) { private void startSearch(String queryString) {
if (getActivity() instanceof AcceptsSearchQuery) { if (getActivity() instanceof AcceptsSearchQuery) {
searchBar.setText(queryString); editText.setText(queryString);
searchBar.setSelection(queryString.length()); editText.setSelection(queryString.length());
transitionToWaiting(); transitionToWaiting();
((AcceptsSearchQuery) getActivity()).onSearch(queryString); ((AcceptsSearchQuery) getActivity()).onSearch(queryString);
} else { } else {
@ -191,12 +208,13 @@ public class SearchFragment extends Fragment
if (state == State.WAITING) { if (state == State.WAITING) {
return; return;
} }
searchBar.setFocusable(false);
searchBar.setFocusableInTouchMode(false); setEditTextFocusable(false);
searchBar.clearFocus(); mainView.setClickable(false);
inputMethodManager.hideSoftInputFromWindow(searchBar.getWindowToken(), 0);
suggestionDropdown.setVisibility(View.GONE); suggestionDropdown.setVisibility(View.GONE);
backdropFrame.setVisibility(View.GONE); clearButton.setVisibility(View.GONE);
state = State.WAITING; state = State.WAITING;
} }
@ -204,20 +222,34 @@ public class SearchFragment extends Fragment
if (state == State.RUNNING) { if (state == State.RUNNING) {
return; return;
} }
searchBar.setFocusable(true);
searchBar.setFocusableInTouchMode(true); setEditTextFocusable(true);
searchBar.requestFocus(); mainView.setClickable(true);
inputMethodManager.showSoftInput(searchBar, InputMethodManager.SHOW_IMPLICIT);
suggestionDropdown.setVisibility(View.VISIBLE); suggestionDropdown.setVisibility(View.VISIBLE);
backdropFrame.setVisibility(View.VISIBLE); clearButton.setVisibility(View.VISIBLE);
state = State.RUNNING; 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 @Override
public void onJumpTap(String suggestion) { public void onJumpTap(String suggestion) {
searchBar.setText(suggestion); editText.setText(suggestion);
// Move cursor to end of search input. // Move cursor to end of search input.
searchBar.setSelection(suggestion.length()); editText.setSelection(suggestion.length());
} }
private class SuggestionLoaderCallbacks implements LoaderManager.LoaderCallbacks<List<String>> { private class SuggestionLoaderCallbacks implements LoaderManager.LoaderCallbacks<List<String>> {
@ -292,18 +324,4 @@ public class SearchFragment extends Fragment
suggestions = null; suggestions = null;
} }
} }
/**
* Click handler for the backdrop. This should:
* - Remove focus from the search bar
* - Hide the keyboard
* - Hide the backdrop
* - Hide the suggestion box.
*/
private class BackdropClickListener implements View.OnClickListener {
@Override
public void onClick(View v) {
transitionToWaiting();
}
}
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 473 B

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 679 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

View File

@ -14,7 +14,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:name="org.mozilla.search.PostSearchFragment" android:name="org.mozilla.search.PostSearchFragment"
android:layout_marginTop="@dimen/webview_height_offset" android:layout_marginTop="@dimen/search_bar_height"
android:id="@+id/postsearch" android:id="@+id/postsearch"
/> />
@ -22,14 +22,13 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:name="org.mozilla.search.PreSearchFragment" android:name="org.mozilla.search.PreSearchFragment"
android:layout_marginTop="@dimen/search_bar_height"
android:id="@+id/presearch" android:id="@+id/presearch"
/> />
<fragment <fragment
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:name="org.mozilla.search.autocomplete.SearchFragment" android:name="org.mozilla.search.autocomplete.SearchFragment"/>
android:id="@+id/header_fragments"
/>
</merge> </merge>

View File

@ -2,51 +2,49 @@
- License, v. 2.0. If a copy of the MPL was not distributed with this - 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/. --> - file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" <LinearLayout
xmlns:tools="http://schemas.android.com/tools" xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" xmlns:tools="http://schemas.android.com/tools"
android:layout_height="match_parent" android:layout_width="match_parent"
tools:context=".autocomplete.AutoCompleteFragment" android:layout_height="match_parent"
> android:orientation="vertical"
android:clickable="false"
tools:context=".autocomplete.AutoCompleteFragment">
<FrameLayout <FrameLayout
android:id="@+id/auto_complete_backdrop" android:id="@+id/search_bar"
android:layout_width="fill_parent" android:layout_width="match_parent"
android:layout_height="fill_parent"
android:background="#a71f1f1f"
android:clickable="true"
android:focusable="true"/>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:background="@drawable/search_card_background" android:padding="10dp">
android:orientation="vertical"
>
<FrameLayout <EditText
android:layout_width="fill_parent" android:id="@+id/search_bar_edit_text"
android:layout_height="wrap_content"> android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionSearch"
android:inputType="textNoSuggestions|textVisiblePassword"
android:drawableLeft="@drawable/search_icon"
android:drawablePadding="10dp"
android:focusable="false"
android:focusableInTouchMode="false"
android:hint="@string/search_bar_hint"/>
<EditText <Button
android:id="@+id/auto_complete_search_bar" android:id="@+id/search_bar_clear_button"
style="@style/AutoCompleteEditText"/> android:layout_width="14dp"
android:layout_height="14dp"
android:layout_gravity="right|center_vertical"
android:layout_marginRight="10dp"
android:background="@drawable/search_clear"
android:visibility="gone"/>
<Button </FrameLayout>
android:id="@+id/clear_button"
android:layout_width="26dp"
android:layout_height="26dp"
android:layout_gravity="right|center_vertical"
android:layout_marginRight="3dp"
android:background="@drawable/search_clear" />
</FrameLayout> <ListView
android:id="@+id/auto_complete_dropdown"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#fff"
android:visibility="gone"/>
<ListView </LinearLayout>
android:id="@+id/auto_complete_dropdown"
style="@style/AutoCompleteDropdown"/>
</LinearLayout>
</FrameLayout>

View File

@ -1,12 +0,0 @@
<!-- 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/. -->
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="100dp"
android:scaleType="centerCrop"
android:src="@drawable/search_header"
android:contentDescription="@string/search_header_image_content_description">
</ImageView>

View File

@ -6,7 +6,7 @@
<color name="transparent">#00FFFFFF</color> <color name="transparent">#00FFFFFF</color>
<color name="highlight_orange">#FF9500</color>
<!-- card colors --> <!-- card colors -->
<color name="card_background">#ffffff</color> <color name="card_background">#ffffff</color>

View File

@ -6,5 +6,5 @@
<!--This is used to offset the webview so that it is not covered--> <!--This is used to offset the webview so that it is not covered-->
<!--by the search bar. If we change the height of the search bar--> <!--by the search bar. If we change the height of the search bar-->
<!--then this will need to be updated.--> <!--then this will need to be updated.-->
<dimen name="webview_height_offset">55dp</dimen> <dimen name="search_bar_height">55dp</dimen>
</resources> </resources>

View File

@ -13,19 +13,4 @@
<style name="BorderLessButton"> <style name="BorderLessButton">
</style> </style>
<style name="AutoCompleteDropdown">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">#fff</item>
<item name="android:layout_margin">10dp</item>
</style>
<style name="AutoCompleteEditText">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:imeOptions">actionSearch</item>
<item name="android:inputType">textNoSuggestions|textVisiblePassword</item>
</style>
</resources> </resources>

View File

@ -5,3 +5,4 @@
<!ENTITY search_jump_arrow '&#8598;'> <!ENTITY search_jump_arrow '&#8598;'>
<!ENTITY search_app_name 'Firefox Search'> <!ENTITY search_app_name 'Firefox Search'>
<!ENTITY search_header_image_content_description 'Firefox Search Header Image'> <!ENTITY search_header_image_content_description 'Firefox Search Header Image'>
<!ENTITY search_bar_hint 'Search for anything'>

View File

@ -1,3 +1,4 @@
<string name="search_app_name">&search_app_name;</string> <string name="search_app_name">&search_app_name;</string>
<string name="search_jump_arrow">&search_jump_arrow;</string> <string name="search_jump_arrow">&search_jump_arrow;</string>
<string name="search_header_image_content_description">&search_header_image_content_description;</string> <string name="search_header_image_content_description">&search_header_image_content_description;</string>
<string name="search_bar_hint">&search_bar_hint;</string>