diff options
Diffstat (limited to 'mobile/android/search/java/org')
12 files changed, 0 insertions, 1924 deletions
diff --git a/mobile/android/search/java/org/mozilla/search/AcceptsSearchQuery.java b/mobile/android/search/java/org/mozilla/search/AcceptsSearchQuery.java deleted file mode 100644 index e54b9a9fc..000000000 --- a/mobile/android/search/java/org/mozilla/search/AcceptsSearchQuery.java +++ /dev/null @@ -1,48 +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/. */ - -package org.mozilla.search; - -import android.graphics.Rect; - -/** - * Allows fragments to pass a search event to the main activity. - */ -public interface AcceptsSearchQuery { - - /** - * Shows search suggestions. - * @param query - */ - void onSuggest(String query); - - /** - * Starts a search. - * - * @param query - */ - void onSearch(String query); - - /** - * Starts a search and animates a suggestion. - * - * @param query - * @param suggestionAnimation - */ - void onSearch(String query, SuggestionAnimation suggestionAnimation); - - /** - * Handles a change to the current search query. - * - * @param query - */ - void onQueryChange(String query); - - /** - * Interface to specify search suggestion animation details. - */ - public interface SuggestionAnimation { - public Rect getStartBounds(); - } -} diff --git a/mobile/android/search/java/org/mozilla/search/Constants.java b/mobile/android/search/java/org/mozilla/search/Constants.java deleted file mode 100644 index 8e8a17600..000000000 --- a/mobile/android/search/java/org/mozilla/search/Constants.java +++ /dev/null @@ -1,20 +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/. - */ - -/* 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; - -/** - * Key should not be stored here. For more info on storing keys, see - * https://github.com/ericedens/FirefoxSearch/issues/3 - */ -public class Constants { - - public static final String ABOUT_BLANK = "about:blank"; -} diff --git a/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java b/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java deleted file mode 100644 index 8a26c49dd..000000000 --- a/mobile/android/search/java/org/mozilla/search/PostSearchFragment.java +++ /dev/null @@ -1,243 +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/. */ - -package org.mozilla.search; - -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; - -import android.support.v4.content.ContextCompat; -import org.mozilla.gecko.AppConstants; -import org.mozilla.gecko.GeckoApplication; -import org.mozilla.gecko.R; -import org.mozilla.gecko.search.SearchEngine; -import org.mozilla.gecko.Telemetry; -import org.mozilla.gecko.TelemetryContract; - -import android.annotation.SuppressLint; -import android.content.ActivityNotFoundException; -import android.content.Intent; -import android.graphics.Bitmap; -import android.os.Bundle; -import android.provider.Settings; -import android.support.v4.app.Fragment; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewStub; -import android.webkit.WebChromeClient; -import android.webkit.WebView; -import android.webkit.WebViewClient; -import android.widget.ImageView; -import android.widget.ProgressBar; -import android.widget.TextView; - -public class PostSearchFragment extends Fragment { - - private static final String LOG_TAG = "PostSearchFragment"; - - private SearchEngine engine; - - private ProgressBar progressBar; - private WebView webview; - private View errorView; - - private String resultsPageHost; - - @SuppressLint("SetJavaScriptEnabled") - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - View mainView = inflater.inflate(R.layout.search_fragment_post_search, container, false); - - progressBar = (ProgressBar) mainView.findViewById(R.id.progress_bar); - - webview = (WebView) mainView.findViewById(R.id.webview); - webview.setWebChromeClient(new ChromeClient()); - webview.setWebViewClient(new ResultsWebViewClient()); - - // This is required for our greasemonkey terror script. - webview.getSettings().setJavaScriptEnabled(true); - - return mainView; - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - webview.setWebChromeClient(null); - webview.setWebViewClient(null); - webview = null; - progressBar = null; - } - - @Override - public void onDestroy() { - super.onDestroy(); - - GeckoApplication.watchReference(getActivity(), this); - } - - public void startSearch(SearchEngine engine, String query) { - this.engine = engine; - - final String url = engine.resultsUriForQuery(query); - // Only load urls if the url is different than the webview's current url. - if (!TextUtils.equals(webview.getUrl(), url)) { - resultsPageHost = null; - webview.loadUrl(Constants.ABOUT_BLANK); - webview.loadUrl(url); - } - } - - /** - * A custom WebViewClient that intercepts every page load. This allows - * us to decide whether to load the url here, or send it to Android - * as an intent. It also handles network errors. - */ - private class ResultsWebViewClient extends WebViewClient { - - // Whether or not there is a network error. - private boolean networkError; - - @Override - public void onPageStarted(WebView view, final String url, Bitmap favicon) { - // Reset the error state. - networkError = false; - } - - @Override - public boolean shouldOverrideUrlLoading(WebView view, String url) { - // Ignore about:blank URL loads and the first results page we try to load. - if (TextUtils.equals(url, Constants.ABOUT_BLANK) || resultsPageHost == null) { - return false; - } - - String host = null; - try { - host = new URL(url).getHost(); - } catch (MalformedURLException e) { - Log.e(LOG_TAG, "Error getting host from URL loading in webview", e); - } - - // If the host name is the same as the results page, don't override the URL load, but - // do update the query in the search bar if possible. - if (TextUtils.equals(resultsPageHost, host)) { - // This won't work for results pages that redirect (e.g. Google in different country) - final String query = engine.queryForResultsUrl(url); - if (!TextUtils.isEmpty(query)) { - ((AcceptsSearchQuery) getActivity()).onQueryChange(query); - } - return false; - } - - try { - // If the url URI does not have an intent scheme, the intent data will be the entire - // URI and its action will be ACTION_VIEW. - final Intent i = Intent.parseUri(url, Intent.URI_INTENT_SCHEME); - - // If the intent URI didn't specify a package, open this in Fennec. - if (i.getPackage() == null) { - i.setClassName(view.getContext().getPackageName(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS); - Telemetry.sendUIEvent(TelemetryContract.Event.LOAD_URL, - TelemetryContract.Method.CONTENT, "search-result"); - } else { - Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, - TelemetryContract.Method.INTENT, "search-result"); - } - - i.addCategory(Intent.CATEGORY_BROWSABLE); - i.setComponent(null); - i.setSelector(null); - - startActivity(i); - return true; - } catch (URISyntaxException e) { - Log.e(LOG_TAG, "Error parsing intent URI", e); - } catch (SecurityException e) { - Log.e(LOG_TAG, "SecurityException handling arbitrary intent content"); - } catch (ActivityNotFoundException e) { - Log.e(LOG_TAG, "Intent not actionable"); - } - - return false; - } - - // We are suppressing the 'deprecation' warning because the new method is only available starting with API - // level 23 and that's much higher than our current minSdkLevel (1208580). - @SuppressWarnings("deprecation") - @Override - public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { - Log.e(LOG_TAG, "Error loading search results: " + description); - - networkError = true; - - if (errorView == null) { - final ViewStub errorViewStub = (ViewStub) getView().findViewById(R.id.error_view_stub); - errorView = errorViewStub.inflate(); - - ((ImageView) errorView.findViewById(R.id.empty_image)).setImageResource(R.drawable.network_error); - ((TextView) errorView.findViewById(R.id.empty_title)).setText(R.string.network_error_title); - - final TextView message = (TextView) errorView.findViewById(R.id.empty_message); - message.setText(R.string.network_error_message); - message.setTextColor(ContextCompat.getColor(view.getContext(), R.color.network_error_link)); - message.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startActivity(new Intent(Settings.ACTION_SETTINGS)); - } - }); - } - } - - @Override - public void onPageFinished(WebView view, String url) { - // Make sure the error view is hidden if the network error was fixed. - if (errorView != null) { - errorView.setVisibility(networkError ? View.VISIBLE : View.GONE); - webview.setVisibility(networkError ? View.GONE : View.VISIBLE); - } - - if (!TextUtils.equals(url, Constants.ABOUT_BLANK) && resultsPageHost == null) { - try { - resultsPageHost = new URL(url).getHost(); - } catch (MalformedURLException e) { - Log.e(LOG_TAG, "Error getting host from results page URL", e); - } - } - } - } - - /** - * A custom WebChromeClient that allows us to inject CSS into - * the head of the HTML and to monitor pageload progress. - * - * We use the WebChromeClient because it provides a hook to the titleReceived - * event. Once the title is available, the page will have started parsing the - * head element. The script injects its CSS into the head element. - */ - private class ChromeClient extends WebChromeClient { - - @Override - public void onReceivedTitle(final WebView view, String title) { - view.loadUrl(engine.getInjectableJs()); - } - - @Override - public void onProgressChanged(WebView view, int newProgress) { - if (newProgress < 100) { - if (progressBar.getVisibility() == View.INVISIBLE) { - progressBar.setVisibility(View.VISIBLE); - } - progressBar.setProgress(newProgress); - } else { - progressBar.setVisibility(View.INVISIBLE); - } - } - } -} diff --git a/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java b/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java deleted file mode 100644 index 107b82c5c..000000000 --- a/mobile/android/search/java/org/mozilla/search/PreSearchFragment.java +++ /dev/null @@ -1,218 +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/. */ - -package org.mozilla.search; - -import android.content.Context; -import android.database.Cursor; -import android.graphics.Rect; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.app.LoaderManager; -import android.support.v4.content.CursorLoader; -import android.support.v4.content.Loader; -import android.support.v4.widget.SimpleCursorAdapter; -import android.text.TextUtils; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.ViewStub; -import android.widget.AdapterView; -import android.widget.ImageView; -import android.widget.ListView; -import android.widget.TextView; - -import org.mozilla.gecko.GeckoApplication; -import org.mozilla.gecko.R; -import org.mozilla.gecko.Telemetry; -import org.mozilla.gecko.TelemetryContract; -import org.mozilla.gecko.db.BrowserContract; -import org.mozilla.gecko.db.BrowserContract.SearchHistory; -import org.mozilla.gecko.widget.SwipeDismissListViewTouchListener; -import org.mozilla.gecko.widget.SwipeDismissListViewTouchListener.OnDismissCallback; -import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation; - -/** - * This fragment is responsible for managing the card stream. - */ -public class PreSearchFragment extends Fragment { - - private static final String LOG_TAG = "PreSearchFragment"; - - private AcceptsSearchQuery searchListener; - private SimpleCursorAdapter cursorAdapter; - - private ListView listView; - private View emptyView; - - private static final String[] PROJECTION = new String[]{ SearchHistory.QUERY, SearchHistory._ID }; - - // Limit search history query results to 10 items. - private static final int SEARCH_HISTORY_LIMIT = 10; - - private static final Uri SEARCH_HISTORY_URI = SearchHistory.CONTENT_URI.buildUpon(). - appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(SEARCH_HISTORY_LIMIT)).build(); - - private static final int LOADER_ID_SEARCH_HISTORY = 1; - - public PreSearchFragment() { - // Mandatory empty constructor for Android's Fragment. - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - - if (context instanceof AcceptsSearchQuery) { - searchListener = (AcceptsSearchQuery) context; - } else { - throw new ClassCastException(context.toString() + " must implement AcceptsSearchQuery."); - } - } - - @Override - public void onDetach() { - super.onDetach(); - searchListener = null; - } - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - getLoaderManager().initLoader(LOADER_ID_SEARCH_HISTORY, null, new SearchHistoryLoaderCallbacks()); - cursorAdapter = new SimpleCursorAdapter(getActivity(), R.layout.search_history_row, null, - PROJECTION, new int[]{R.id.site_name}, 0); - } - - @Override - public void onDestroy() { - super.onDestroy(); - getLoaderManager().destroyLoader(LOADER_ID_SEARCH_HISTORY); - cursorAdapter.swapCursor(null); - cursorAdapter = null; - - GeckoApplication.watchReference(getActivity(), this); - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedState) { - final View mainView = inflater.inflate(R.layout.search_fragment_pre_search, container, false); - - // Initialize listview. - listView = (ListView) mainView.findViewById(R.id.list_view); - listView.setAdapter(cursorAdapter); - listView.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - final String query = getQueryAtPosition(position); - if (!TextUtils.isEmpty(query)) { - final Rect startBounds = new Rect(); - view.getGlobalVisibleRect(startBounds); - - Telemetry.sendUIEvent(TelemetryContract.Event.SEARCH, TelemetryContract.Method.SUGGESTION, "history"); - - searchListener.onSearch(query, new SuggestionAnimation() { - @Override - public Rect getStartBounds() { - return startBounds; - } - }); - } - } - }); - - // Create a ListView-specific touch listener. ListViews are given special treatment because - // by default they handle touches for their list items... i.e. they're in charge of drawing - // the pressed state (the list selector), handling list item clicks, etc. - final SwipeDismissListViewTouchListener touchListener = new SwipeDismissListViewTouchListener(listView, new OnDismissCallback() { - @Override - public void onDismiss(ListView listView, final int position) { - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - final String query = getQueryAtPosition(position); - final int deleted = getActivity().getContentResolver().delete( - SearchHistory.CONTENT_URI, - SearchHistory.QUERY + " = ?", - new String[] { query }); - - if (deleted < 1) { - Log.w(LOG_TAG, "Search query not deleted: " + query); - } - return null; - } - }.execute(); - } - }); - listView.setOnTouchListener(touchListener); - - // Setting this scroll listener is required to ensure that during ListView scrolling, - // we don't look for swipes. - listView.setOnScrollListener(touchListener.makeScrollListener()); - - // Setting this recycler listener is required to make sure animated views are reset. - listView.setRecyclerListener(touchListener.makeRecyclerListener()); - - return mainView; - } - - private String getQueryAtPosition(int position) { - final Cursor c = cursorAdapter.getCursor(); - if (c == null || !c.moveToPosition(position)) { - return null; - } - return c.getString(c.getColumnIndexOrThrow(SearchHistory.QUERY)); - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - listView.setAdapter(null); - listView = null; - emptyView = null; - } - - private void updateUiFromCursor(Cursor c) { - if (c != null && c.getCount() > 0) { - return; - } - - if (emptyView == null) { - final ViewStub emptyViewStub = (ViewStub) getView().findViewById(R.id.empty_view_stub); - emptyView = emptyViewStub.inflate(); - - ((ImageView) emptyView.findViewById(R.id.empty_image)).setImageResource(R.drawable.icon_search_empty_firefox); - ((TextView) emptyView.findViewById(R.id.empty_title)).setText(R.string.search_empty_title); - ((TextView) emptyView.findViewById(R.id.empty_message)).setText(R.string.search_empty_message); - - listView.setEmptyView(emptyView); - } - } - - private class SearchHistoryLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> { - @Override - public Loader<Cursor> onCreateLoader(int id, Bundle args) { - return new CursorLoader(getActivity(), SEARCH_HISTORY_URI, PROJECTION, null, null, - SearchHistory.DATE_LAST_VISITED + " DESC"); - } - - @Override - public void onLoadFinished(Loader<Cursor> loader, Cursor c) { - if (cursorAdapter != null) { - cursorAdapter.swapCursor(c); - } - updateUiFromCursor(c); - } - - @Override - public void onLoaderReset(Loader<Cursor> loader) { - if (cursorAdapter != null) { - cursorAdapter.swapCursor(null); - } - } - } -} diff --git a/mobile/android/search/java/org/mozilla/search/SearchActivity.java b/mobile/android/search/java/org/mozilla/search/SearchActivity.java deleted file mode 100644 index b013d77b4..000000000 --- a/mobile/android/search/java/org/mozilla/search/SearchActivity.java +++ /dev/null @@ -1,436 +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/. */ - -package org.mozilla.search; - -import android.support.annotation.NonNull; -import org.mozilla.gecko.GeckoAppShell; -import org.mozilla.gecko.Locales; -import org.mozilla.gecko.R; -import org.mozilla.gecko.Telemetry; -import org.mozilla.gecko.TelemetryContract; -import org.mozilla.gecko.db.BrowserContract.SearchHistory; -import org.mozilla.gecko.distribution.Distribution; -import org.mozilla.gecko.search.SearchEngine; -import org.mozilla.gecko.search.SearchEngineManager; -import org.mozilla.gecko.search.SearchEngineManager.SearchEngineCallback; -import org.mozilla.search.autocomplete.SearchBar; -import org.mozilla.search.autocomplete.SuggestionsFragment; - -import android.content.AsyncQueryHandler; -import android.content.ContentResolver; -import android.content.ContentValues; -import android.content.Intent; -import android.graphics.Rect; -import android.os.Bundle; -import android.text.TextUtils; -import android.util.Log; -import android.view.View; -import android.view.animation.AccelerateDecelerateInterpolator; -import android.view.animation.Interpolator; - -import android.animation.Animator; -import android.animation.AnimatorSet; -import android.animation.ObjectAnimator; - -/** - * The main entrance for the Android search intent. - * <p/> - * State management is delegated to child fragments. Fragments communicate - * with each other by passing messages through this activity. - */ -public class SearchActivity extends Locales.LocaleAwareFragmentActivity - implements AcceptsSearchQuery, SearchEngineCallback { - - private static final String LOGTAG = "GeckoSearchActivity"; - - private static final String KEY_SEARCH_STATE = "search_state"; - private static final String KEY_EDIT_STATE = "edit_state"; - private static final String KEY_QUERY = "query"; - - static enum SearchState { - PRESEARCH, - POSTSEARCH - } - - static enum EditState { - WAITING, - EDITING - } - - // Default states when activity is created. - private SearchState searchState = SearchState.PRESEARCH; - private EditState editState = EditState.WAITING; - - @NonNull - private SearchEngineManager searchEngineManager; // Contains reference to Context - DO NOT LEAK! - - // Only accessed on the main thread. - private SearchEngine engine; - - private SuggestionsFragment suggestionsFragment; - private PostSearchFragment postSearchFragment; - - private AsyncQueryHandler queryHandler; - - // Main views in layout. - private SearchBar searchBar; - private View preSearch; - private View postSearch; - - private View settingsButton; - - private View suggestions; - - private static final int SUGGESTION_TRANSITION_DURATION = 300; - private static final Interpolator SUGGESTION_TRANSITION_INTERPOLATOR = - new AccelerateDecelerateInterpolator(); - - // View used for suggestion animation. - private View animationCard; - - // Suggestion card background padding. - private int cardPaddingX; - private int cardPaddingY; - - /** - * An empty implementation of AsyncQueryHandler to avoid the "HandlerLeak" warning from Android - * Lint. See also {@see org.mozilla.gecko.util.WeakReferenceHandler}. - */ - private static class AsyncQueryHandlerImpl extends AsyncQueryHandler { - public AsyncQueryHandlerImpl(final ContentResolver that) { - super(that); - } - } - - @Override - protected void onCreate(Bundle savedInstanceState) { - GeckoAppShell.ensureCrashHandling(); - - super.onCreate(savedInstanceState); - setContentView(R.layout.search_activity_main); - - suggestionsFragment = (SuggestionsFragment) getSupportFragmentManager().findFragmentById(R.id.suggestions); - postSearchFragment = (PostSearchFragment) getSupportFragmentManager().findFragmentById(R.id.postsearch); - - searchEngineManager = new SearchEngineManager(this, Distribution.init(getApplicationContext())); - searchEngineManager.setChangeCallback(this); - - // Initialize the fragments with the selected search engine. - searchEngineManager.getEngine(this); - - queryHandler = new AsyncQueryHandlerImpl(getContentResolver()); - - searchBar = (SearchBar) findViewById(R.id.search_bar); - searchBar.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - setEditState(EditState.EDITING); - } - }); - - searchBar.setTextListener(new SearchBar.TextListener() { - @Override - public void onChange(String text) { - // Only load suggestions if we're in edit mode. - if (editState == EditState.EDITING) { - suggestionsFragment.loadSuggestions(text); - } - } - - @Override - public void onSubmit(String text) { - // Don't submit an empty query. - final String trimmedQuery = text.trim(); - if (!TextUtils.isEmpty(trimmedQuery)) { - onSearch(trimmedQuery); - } - } - - @Override - public void onFocusChange(boolean hasFocus) { - setEditState(hasFocus ? EditState.EDITING : EditState.WAITING); - } - }); - - preSearch = findViewById(R.id.presearch); - postSearch = findViewById(R.id.postsearch); - - settingsButton = findViewById(R.id.settings_button); - - // Apply click handler to settings button. - settingsButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - startActivity(new Intent(SearchActivity.this, SearchPreferenceActivity.class)); - } - }); - - suggestions = findViewById(R.id.suggestions); - - animationCard = findViewById(R.id.animation_card); - - cardPaddingX = getResources().getDimensionPixelSize(R.dimen.search_row_padding); - cardPaddingY = getResources().getDimensionPixelSize(R.dimen.search_row_padding); - - if (savedInstanceState != null) { - setSearchState(SearchState.valueOf(savedInstanceState.getString(KEY_SEARCH_STATE))); - setEditState(EditState.valueOf(savedInstanceState.getString(KEY_EDIT_STATE))); - - final String query = savedInstanceState.getString(KEY_QUERY); - searchBar.setText(query); - - // If we're in the postsearch state, we need to re-do the query. - if (searchState == SearchState.POSTSEARCH) { - startSearch(query); - } - } else { - // If there isn't a state to restore, the activity will start in the presearch state, - // and we should enter editing mode to bring up the keyboard. - setEditState(EditState.EDITING); - } - } - - @Override - protected void onDestroy() { - super.onDestroy(); - searchEngineManager.unregisterListeners(); - engine = null; - suggestionsFragment = null; - postSearchFragment = null; - queryHandler = null; - searchBar = null; - preSearch = null; - postSearch = null; - settingsButton = null; - suggestions = null; - animationCard = null; - } - - @Override - protected void onStart() { - super.onStart(); - Telemetry.startUISession(TelemetryContract.Session.SEARCH_ACTIVITY); - } - - @Override - protected void onStop() { - super.onStop(); - Telemetry.stopUISession(TelemetryContract.Session.SEARCH_ACTIVITY); - } - - @Override - public void onNewIntent(Intent intent) { - // Reset the activity in the presearch state if it was launched from a new intent. - setSearchState(SearchState.PRESEARCH); - - // Enter editing mode and reset the query. We must reset the query after entering - // edit mode in order for the suggestions to update. - setEditState(EditState.EDITING); - searchBar.setText(""); - } - - @Override - public void onSaveInstanceState(Bundle outState) { - super.onSaveInstanceState(outState); - - outState.putString(KEY_SEARCH_STATE, searchState.toString()); - outState.putString(KEY_EDIT_STATE, editState.toString()); - outState.putString(KEY_QUERY, searchBar.getText()); - } - - @Override - public void onSuggest(String query) { - searchBar.setText(query); - } - - @Override - public void onSearch(String query) { - onSearch(query, null); - } - - @Override - public void onSearch(String query, SuggestionAnimation suggestionAnimation) { - storeQuery(query); - - try { - //BrowserHealthRecorder.recordSearchDelayed("activity", engine.getIdentifier()); - } catch (Exception e) { - // This should never happen: it'll only throw if the - // search location is wrong. But let's not tempt fate. - Log.w(LOGTAG, "Unable to record search."); - } - - startSearch(query); - - if (suggestionAnimation != null) { - searchBar.setText(query); - // Animate the suggestion card if start bounds are specified. - animateSuggestion(suggestionAnimation); - } else { - // Otherwise immediately switch to the results view. - setEditState(EditState.WAITING); - setSearchState(SearchState.POSTSEARCH); - } - } - - @Override - public void onQueryChange(String query) { - searchBar.setText(query); - } - - private void startSearch(final String query) { - if (engine != null) { - postSearchFragment.startSearch(engine, query); - return; - } - - // engine will only be null if startSearch is called before the getEngine - // call in onCreate is completed. - searchEngineManager.getEngine(new SearchEngineCallback() { - @Override - public void execute(SearchEngine engine) { - // TODO: If engine is null, we should show an error message. - if (engine != null) { - postSearchFragment.startSearch(engine, query); - } - } - }); - } - - /** - * This method is called when we fetch the current engine in onCreate, - * as well as whenever the current engine changes. This method will only - * ever be called on the main thread. - * - * @param engine The current search engine. - */ - @Override - public void execute(SearchEngine engine) { - // TODO: If engine is null, we should show an error message. - if (engine == null) { - return; - } - this.engine = engine; - suggestionsFragment.setEngine(engine); - searchBar.setEngine(engine); - } - - /** - * Animates search suggestion item to fill the results view area. - * - * @param suggestionAnimation - */ - private void animateSuggestion(final SuggestionAnimation suggestionAnimation) { - final Rect startBounds = suggestionAnimation.getStartBounds(); - final Rect endBounds = new Rect(); - animationCard.getGlobalVisibleRect(endBounds, null); - - // Vertically translate the animated card to align with the start bounds. - final float cardStartY = startBounds.centerY() - endBounds.centerY(); - - // Account for card background padding when calculating start scale. - final float startScaleX = (float) (startBounds.width() - cardPaddingX * 2) / endBounds.width(); - final float startScaleY = (float) (startBounds.height() - cardPaddingY * 2) / endBounds.height(); - - animationCard.setVisibility(View.VISIBLE); - - final AnimatorSet set = new AnimatorSet(); - set.playTogether( - ObjectAnimator.ofFloat(animationCard, "translationY", cardStartY, 0), - ObjectAnimator.ofFloat(animationCard, "alpha", 0.5f, 1), - ObjectAnimator.ofFloat(animationCard, "scaleX", startScaleX, 1f), - ObjectAnimator.ofFloat(animationCard, "scaleY", startScaleY, 1f) - ); - - set.addListener(new Animator.AnimatorListener() { - @Override - public void onAnimationStart(Animator animation) { - } - - @Override - public void onAnimationEnd(Animator animation) { - // Don't do anything if the activity is destroyed before the animation ends. - if (searchEngineManager == null) { - return; - } - - setEditState(EditState.WAITING); - setSearchState(SearchState.POSTSEARCH); - - // We need to manually clear the animation for the views to be hidden on gingerbread. - animationCard.clearAnimation(); - animationCard.setVisibility(View.INVISIBLE); - } - - @Override - public void onAnimationCancel(Animator animation) { - } - - @Override - public void onAnimationRepeat(Animator animation) { - } - }); - - set.setDuration(SUGGESTION_TRANSITION_DURATION); - set.setInterpolator(SUGGESTION_TRANSITION_INTERPOLATOR); - - set.start(); - } - - private void setEditState(EditState editState) { - if (this.editState == editState) { - return; - } - this.editState = editState; - - updateSettingsButtonVisibility(); - - searchBar.setActive(editState == EditState.EDITING); - suggestions.setVisibility(editState == EditState.EDITING ? View.VISIBLE : View.INVISIBLE); - } - - private void setSearchState(SearchState searchState) { - if (this.searchState == searchState) { - return; - } - this.searchState = searchState; - - updateSettingsButtonVisibility(); - - preSearch.setVisibility(searchState == SearchState.PRESEARCH ? View.VISIBLE : View.INVISIBLE); - postSearch.setVisibility(searchState == SearchState.POSTSEARCH ? View.VISIBLE : View.INVISIBLE); - } - - private void updateSettingsButtonVisibility() { - // Show button on launch screen when keyboard is down. - if (searchState == SearchState.PRESEARCH && editState == EditState.WAITING) { - settingsButton.setVisibility(View.VISIBLE); - } else { - settingsButton.setVisibility(View.INVISIBLE); - } - } - - @Override - public void onBackPressed() { - if (editState == EditState.EDITING) { - setEditState(EditState.WAITING); - } else if (searchState == SearchState.POSTSEARCH) { - setSearchState(SearchState.PRESEARCH); - } else { - super.onBackPressed(); - } - } - - /** - * Store the search query in Fennec's search history database. - */ - private void storeQuery(String query) { - final ContentValues cv = new ContentValues(); - cv.put(SearchHistory.QUERY, query); - // Setting 0 for the token, since we only have one type of insert. - // Setting null for the cookie, since we don't handle the result of the insert. - queryHandler.startInsert(0, null, SearchHistory.CONTENT_URI, cv); - } -} diff --git a/mobile/android/search/java/org/mozilla/search/SearchPreferenceActivity.java b/mobile/android/search/java/org/mozilla/search/SearchPreferenceActivity.java deleted file mode 100644 index 6d33da130..000000000 --- a/mobile/android/search/java/org/mozilla/search/SearchPreferenceActivity.java +++ /dev/null @@ -1,118 +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/. */ - -package org.mozilla.search; - -import org.mozilla.gecko.GeckoSharedPrefs; -import org.mozilla.gecko.Locales; -import org.mozilla.gecko.R; -import org.mozilla.gecko.Telemetry; -import org.mozilla.gecko.TelemetryContract; -import org.mozilla.gecko.db.BrowserContract; - -import android.app.AlertDialog; -import android.content.DialogInterface; -import android.os.AsyncTask; -import android.os.Build; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceActivity; -import android.util.Log; -import android.view.MenuItem; -import android.widget.Toast; - -/** - * This activity allows users to modify the settings for the search activity. - * - * A note on implementation: At the moment, we don't have tablet-specific designs. - * Therefore, this implementation uses the old-style PreferenceActivity. When - * we start optimizing for tablets, we can migrate to Fennec's PreferenceFragment - * implementation. - * - * TODO: Change this to PreferenceFragment when we stop supporting devices older than SDK 11. - */ -public class SearchPreferenceActivity extends PreferenceActivity { - - private static final String LOG_TAG = "SearchPreferenceActivity"; - - public static final String PREF_CLEAR_HISTORY_KEY = "search.not_a_preference.clear_history"; - - @Override - @SuppressWarnings("deprecation") - protected void onCreate(Bundle savedInstanceState) { - Locales.initializeLocale(getApplicationContext()); - super.onCreate(savedInstanceState); - - getPreferenceManager().setSharedPreferencesName(GeckoSharedPrefs.APP_PREFS_NAME); - - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - if (getActionBar() != null) { - getActionBar().setDisplayHomeAsUpEnabled(true); - } - } - } - - @Override - protected void onPostCreate(Bundle savedInstanceState) { - super.onPostCreate(savedInstanceState); - setupPrefsScreen(); - } - - @SuppressWarnings("deprecation") - private void setupPrefsScreen() { - addPreferencesFromResource(R.xml.search_preferences); - - // Attach click listener to clear history button. - final Preference clearHistoryButton = findPreference(PREF_CLEAR_HISTORY_KEY); - clearHistoryButton.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() { - @Override - public boolean onPreferenceClick(Preference preference) { - final AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(SearchPreferenceActivity.this); - dialogBuilder.setNegativeButton(android.R.string.cancel, null); - dialogBuilder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { - @Override - public void onClick(DialogInterface dialog, int which) { - Telemetry.sendUIEvent(TelemetryContract.Event.SANITIZE, TelemetryContract.Method.MENU, "search-history"); - clearHistory(); - } - }); - dialogBuilder.setMessage(R.string.pref_clearHistory_dialogMessage); - dialogBuilder.show(); - return false; - } - }); - } - - private void clearHistory() { - final AsyncTask<Void, Void, Boolean> clearHistoryTask = new AsyncTask<Void, Void, Boolean>() { - @Override - protected Boolean doInBackground(Void... params) { - final int numDeleted = getContentResolver().delete( - BrowserContract.SearchHistory.CONTENT_URI, null, null); - return numDeleted >= 0; - } - - @Override - protected void onPostExecute(Boolean success) { - if (success) { - getContentResolver().notifyChange(BrowserContract.SearchHistory.CONTENT_URI, null); - Toast.makeText(SearchPreferenceActivity.this, SearchPreferenceActivity.this.getResources() - .getString(R.string.pref_clearHistory_confirmation), Toast.LENGTH_SHORT).show(); - } else { - Log.e(LOG_TAG, "Error clearing search history."); - } - } - }; - clearHistoryTask.execute(); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - finish(); - return true; - } - return false; - } -} diff --git a/mobile/android/search/java/org/mozilla/search/SearchWidget.java b/mobile/android/search/java/org/mozilla/search/SearchWidget.java deleted file mode 100644 index 8f69cc22c..000000000 --- a/mobile/android/search/java/org/mozilla/search/SearchWidget.java +++ /dev/null @@ -1,135 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * 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; - -import org.mozilla.gecko.AboutPages; -import org.mozilla.gecko.AppConstants; -import org.mozilla.gecko.R; -import org.mozilla.gecko.Telemetry; -import org.mozilla.gecko.TelemetryContract; - -import android.annotation.TargetApi; -import android.app.PendingIntent; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.appwidget.AppWidgetProviderInfo; -import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.os.Bundle; -import android.widget.RemoteViews; - -/* Provides a really simple widget with two buttons, one to launch Fennec - * and one to launch the search activity. All intents are actually sent back - * here and then forwarded on to start the real activity. */ -public class SearchWidget extends AppWidgetProvider { - final private static String LOGTAG = "GeckoSearchWidget"; - - final public static String ACTION_LAUNCH_BROWSER = "org.mozilla.widget.LAUNCH_BROWSER"; - final public static String ACTION_LAUNCH_SEARCH = "org.mozilla.widget.LAUNCH_SEARCH"; - final public static String ACTION_LAUNCH_NEW_TAB = "org.mozilla.widget.LAUNCH_NEW_TAB"; - - @TargetApi(16) - @Override - public void onUpdate(final Context context, final AppWidgetManager manager, final int[] ids) { - for (int id : ids) { - final Bundle bundle; - if (AppConstants.Versions.feature16Plus) { - bundle = manager.getAppWidgetOptions(id); - } else { - bundle = null; - } - addView(manager, context, id, bundle); - } - - super.onUpdate(context, manager, ids); - } - - @TargetApi(16) - @Override - public void onAppWidgetOptionsChanged(final Context context, - final AppWidgetManager manager, - final int id, - final Bundle options) { - addView(manager, context, id, options); - if (AppConstants.Versions.feature16Plus) { - super.onAppWidgetOptionsChanged(context, manager, id, options); - } - } - - @Override - public void onReceive(final Context context, final Intent intent) { - // This will hold the intent to redispatch. - final Intent redirect; - switch (intent.getAction()) { - case ACTION_LAUNCH_BROWSER: - redirect = buildRedirectIntent(Intent.ACTION_MAIN, - context.getPackageName(), - AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS, - intent); - Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, - TelemetryContract.Method.WIDGET, "browser"); - break; - case ACTION_LAUNCH_NEW_TAB: - redirect = buildRedirectIntent(Intent.ACTION_VIEW, - context.getPackageName(), - AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS, - intent); - Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, - TelemetryContract.Method.WIDGET, "new-tab"); - break; - case ACTION_LAUNCH_SEARCH: - redirect = buildRedirectIntent(Intent.ACTION_VIEW, - context.getPackageName(), - AppConstants.MOZ_ANDROID_SEARCH_INTENT_CLASS, - intent); - Telemetry.sendUIEvent(TelemetryContract.Event.LAUNCH, - TelemetryContract.Method.WIDGET, "search"); - break; - default: - redirect = null; - } - - if (redirect != null) { - context.startActivity(redirect); - } - - super.onReceive(context, intent); - } - - // Utility to create the view for this widget and attach any event listeners to it - private void addView(final AppWidgetManager manager, final Context context, final int id, final Bundle options) { - final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.search_widget); - - addClickIntent(context, views, R.id.search_button, ACTION_LAUNCH_SEARCH); - addClickIntent(context, views, R.id.new_tab_button, ACTION_LAUNCH_NEW_TAB); - // Clicking the logo also launches the browser - addClickIntent(context, views, R.id.logo_button, ACTION_LAUNCH_BROWSER); - - manager.updateAppWidget(id, views); - } - - // Utility for adding a pending intent to be fired when a View is clicked. - private void addClickIntent(final Context context, final RemoteViews views, final int viewId, final String action) { - final Intent intent = new Intent(context, SearchWidget.class); - intent.setAction(action); - intent.setData(Uri.parse(AboutPages.HOME)); - final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0); - views.setOnClickPendingIntent(viewId, pendingIntent); - } - - // Utility for building an intent to be redispatched (i.e. to launch the browser or the search intent). - private Intent buildRedirectIntent(final String action, final String pkg, final String className, final Intent source) { - final Intent activity = new Intent(action); - if (pkg != null && className != null) { - activity.setClassName(pkg, className); - } - activity.setData(source.getData()); - activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - return activity; - } - -} diff --git a/mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteAdapter.java b/mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteAdapter.java deleted file mode 100644 index 5a0cc8fb6..000000000 --- a/mobile/android/search/java/org/mozilla/search/autocomplete/AutoCompleteAdapter.java +++ /dev/null @@ -1,82 +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/. */ - -package org.mozilla.search.autocomplete; - -import java.util.List; - -import org.mozilla.gecko.R; -import org.mozilla.search.AcceptsSearchQuery; -import org.mozilla.search.autocomplete.SuggestionsFragment.Suggestion; - -import android.content.Context; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.TextView; - -/** - * The adapter that is used to populate the autocomplete rows. - */ -class AutoCompleteAdapter extends ArrayAdapter<Suggestion> { - - private final AcceptsSearchQuery searchListener; - - private final LayoutInflater inflater; - - public AutoCompleteAdapter(Context context) { - // Uses '0' for the template id since we are overriding getView - // and supplying our own view. - super(context, 0); - - if (context instanceof AcceptsSearchQuery) { - searchListener = (AcceptsSearchQuery) context; - } else { - throw new ClassCastException(context.toString() + " must implement AcceptsSearchQuery."); - } - - // Disable notifying on change. We will notify ourselves in update. - setNotifyOnChange(false); - - inflater = LayoutInflater.from(context); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - if (convertView == null) { - convertView = inflater.inflate(R.layout.search_suggestions_row, null); - } - - final Suggestion suggestion = getItem(position); - - final TextView textView = (TextView) convertView.findViewById(R.id.auto_complete_row_text); - textView.setText(suggestion.display); - - final View jumpButton = convertView.findViewById(R.id.auto_complete_row_jump_button); - jumpButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - searchListener.onSuggest(suggestion.value); - } - }); - - return convertView; - } - - /** - * Updates adapter content with new list of search suggestions. - * - * @param suggestions List of search suggestions. - */ - public void update(List<Suggestion> suggestions) { - clear(); - if (suggestions != null) { - for (Suggestion s : suggestions) { - add(s); - } - } - notifyDataSetChanged(); - } -} diff --git a/mobile/android/search/java/org/mozilla/search/autocomplete/SearchBar.java b/mobile/android/search/java/org/mozilla/search/autocomplete/SearchBar.java deleted file mode 100644 index 6225c050b..000000000 --- a/mobile/android/search/java/org/mozilla/search/autocomplete/SearchBar.java +++ /dev/null @@ -1,201 +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/. */ - -package org.mozilla.search.autocomplete; - -import org.mozilla.gecko.R; -import org.mozilla.gecko.Telemetry; -import org.mozilla.gecko.TelemetryContract; -import org.mozilla.gecko.gfx.BitmapUtils; -import org.mozilla.gecko.search.SearchEngine; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.Color; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffColorFilter; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -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.EditText; -import android.widget.FrameLayout; -import android.widget.ImageButton; -import android.widget.ImageView; -import android.widget.TextView; - -public class SearchBar extends FrameLayout { - - private final EditText editText; - private final ImageButton clearButton; - private final ImageView engineIcon; - - private final Drawable focusedBackground; - private final Drawable defaultBackground; - - private final InputMethodManager inputMethodManager; - - private TextListener listener; - - private boolean active; - - public interface TextListener { - public void onChange(String text); - public void onSubmit(String text); - public void onFocusChange(boolean hasFocus); - } - - // Deprecation warnings suppressed to allow building with API level 22 - @SuppressWarnings("deprecation") - public SearchBar(Context context, AttributeSet attrs) { - super(context, attrs); - - LayoutInflater.from(context).inflate(R.layout.search_bar, 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()); - } - - updateClearButtonVisibility(); - } - }); - - // 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_UNSPECIFIED || actionId == EditorInfo.IME_ACTION_SEARCH)) { - // The user searched without using search engine suggestions. - Telemetry.sendUIEvent(TelemetryContract.Event.SEARCH, TelemetryContract.Method.ACTIONBAR, "text"); - listener.onSubmit(v.getText().toString()); - return true; - } - return false; - } - }); - - editText.setOnFocusChangeListener(new View.OnFocusChangeListener() { - @Override - public void onFocusChange(View v, boolean hasFocus) { - if (listener != null) { - listener.onFocusChange(hasFocus); - } - } - }); - - clearButton = (ImageButton) findViewById(R.id.clear_button); - clearButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - editText.setText(""); - } - }); - engineIcon = (ImageView) findViewById(R.id.engine_icon); - - focusedBackground = getResources().getDrawable(R.drawable.edit_text_focused); - defaultBackground = getResources().getDrawable(R.drawable.edit_text_default); - - 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 String getText() { - return editText.getText().toString(); - } - - public void setEngine(SearchEngine engine) { - final String iconURL = engine.getIconURL(); - final Bitmap bitmap = BitmapUtils.getBitmapFromDataURI(iconURL); - final BitmapDrawable d = new BitmapDrawable(getResources(), bitmap); - engineIcon.setImageDrawable(d); - engineIcon.setContentDescription(engine.getName()); - - // Update the focused background color. - int color = BitmapUtils.getDominantColor(bitmap); - - // BitmapUtils#getDominantColor ignores black and white pixels, but it will - // return white if no dominant color was found. We don't want to create a - // white underline for the search bar, so we default to black instead. - if (color == Color.WHITE) { - color = Color.BLACK; - } - focusedBackground.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.MULTIPLY)); - - editText.setHint(getResources().getString(R.string.search_bar_hint, engine.getName())); - } - - @SuppressWarnings("deprecation") - public void setActive(boolean active) { - if (this.active == active) { - return; - } - this.active = active; - - updateClearButtonVisibility(); - - editText.setFocusable(active); - editText.setFocusableInTouchMode(active); - - final int leftDrawable = active ? R.drawable.search_icon_active : R.drawable.search_icon_inactive; - editText.setCompoundDrawablesWithIntrinsicBounds(leftDrawable, 0, 0, 0); - - // We can't use a selector drawable because we apply a color filter to the focused - // background at run time. - // TODO: setBackgroundDrawable is deprecated in API level 16 - editText.setBackgroundDrawable(active ? focusedBackground : defaultBackground); - - if (active) { - editText.requestFocus(); - inputMethodManager.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT); - } else { - editText.clearFocus(); - inputMethodManager.hideSoftInputFromWindow(editText.getWindowToken(), 0); - } - } - - private void updateClearButtonVisibility() { - // Only show the clear button when there is text in the input. - final boolean visible = active && (editText.getText().length() > 0); - clearButton.setVisibility(visible ? View.VISIBLE : View.GONE); - engineIcon.setVisibility(visible ? View.GONE : View.VISIBLE); - } - - 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/SuggestionsFragment.java b/mobile/android/search/java/org/mozilla/search/autocomplete/SuggestionsFragment.java deleted file mode 100644 index ce935e437..000000000 --- a/mobile/android/search/java/org/mozilla/search/autocomplete/SuggestionsFragment.java +++ /dev/null @@ -1,263 +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/. */ - -package org.mozilla.search.autocomplete; - -import java.util.ArrayList; -import java.util.List; - -import android.support.v4.content.ContextCompat; -import org.mozilla.gecko.R; -import org.mozilla.gecko.search.SearchEngine; -import org.mozilla.gecko.SuggestClient; -import org.mozilla.gecko.Telemetry; -import org.mozilla.gecko.TelemetryContract; -import org.mozilla.search.AcceptsSearchQuery; -import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation; - -import android.content.Context; -import android.graphics.Rect; -import android.os.Bundle; -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.SpannableString; -import android.text.style.ForegroundColorSpan; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.AdapterView; -import android.widget.ListView; - -/** - * A fragment to show search suggestions. - */ -public class SuggestionsFragment extends Fragment { - - private static final String LOG_TAG = "SuggestionsFragment"; - - private static final int LOADER_ID_SUGGESTION = 0; - private static final String KEY_SEARCH_TERM = "search_term"; - - // Timeout for the suggestion client to respond - private static final int SUGGESTION_TIMEOUT = 3000; - - // Number of search suggestions to show. - private static final int SUGGESTION_MAX = 5; - - public static final String GECKO_SEARCH_TERMS_URL_PARAM = "__searchTerms__"; - - private AcceptsSearchQuery searchListener; - - // Suggest client gets setup outside of the normal fragment lifecycle, therefore - // clients should ensure that this isn't null before using it. - private SuggestClient suggestClient; - private SuggestionLoaderCallbacks suggestionLoaderCallbacks; - - private AutoCompleteAdapter autoCompleteAdapter; - - // Holds the list of search suggestions. - private ListView suggestionsList; - - public SuggestionsFragment() { - // Required empty public constructor - } - - @Override - public void onAttach(Context context) { - super.onAttach(context); - - if (context instanceof AcceptsSearchQuery) { - searchListener = (AcceptsSearchQuery) context; - } else { - throw new ClassCastException(context.toString() + " must implement AcceptsSearchQuery."); - } - - suggestionLoaderCallbacks = new SuggestionLoaderCallbacks(); - autoCompleteAdapter = new AutoCompleteAdapter(context); - } - - @Override - public void onDetach() { - super.onDetach(); - - searchListener = null; - suggestionLoaderCallbacks = null; - autoCompleteAdapter = null; - suggestClient = null; - } - - @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, - Bundle savedInstanceState) { - suggestionsList = (ListView) inflater.inflate(R.layout.search_sugestions, container, false); - suggestionsList.setAdapter(autoCompleteAdapter); - - // Attach listener for tapping on a suggestion. - suggestionsList.setOnItemClickListener(new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - final Suggestion suggestion = (Suggestion) suggestionsList.getItemAtPosition(position); - - final Rect startBounds = new Rect(); - view.getGlobalVisibleRect(startBounds); - - // The user tapped on a suggestion from the search engine. - Telemetry.sendUIEvent(TelemetryContract.Event.SEARCH, TelemetryContract.Method.SUGGESTION, position); - - searchListener.onSearch(suggestion.value, new SuggestionAnimation() { - @Override - public Rect getStartBounds() { - return startBounds; - } - }); - } - }); - - return suggestionsList; - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - - if (null != suggestionsList) { - suggestionsList.setOnItemClickListener(null); - suggestionsList.setAdapter(null); - suggestionsList = null; - } - } - - public void setEngine(SearchEngine engine) { - suggestClient = new SuggestClient(getActivity(), engine.getSuggestionTemplate(GECKO_SEARCH_TERMS_URL_PARAM), - SUGGESTION_TIMEOUT, SUGGESTION_MAX, true); - } - - public void loadSuggestions(String query) { - final Bundle args = new Bundle(); - args.putString(KEY_SEARCH_TERM, query); - final LoaderManager loaderManager = getLoaderManager(); - - // Ensure that we don't try to restart a loader that doesn't exist. This becomes - // an issue because SuggestionLoaderCallbacks.onCreateLoader can return null - // as a loader if we don't have a suggestClient available yet. - if (loaderManager.getLoader(LOADER_ID_SUGGESTION) == null) { - loaderManager.initLoader(LOADER_ID_SUGGESTION, args, suggestionLoaderCallbacks); - } else { - loaderManager.restartLoader(LOADER_ID_SUGGESTION, args, suggestionLoaderCallbacks); - } - } - - public static class Suggestion { - - public final String value; - public final SpannableString display; - public final ForegroundColorSpan colorSpan; - - public Suggestion(String value, String searchTerm, int suggestionHighlightColor) { - this.value = value; - - display = new SpannableString(value); - - colorSpan = new ForegroundColorSpan(suggestionHighlightColor); - - // Highlight mixed-case matches. - final int start = value.toLowerCase().indexOf(searchTerm.toLowerCase()); - if (start >= 0) { - display.setSpan(colorSpan, start, start + searchTerm.length(), 0); - } - } - } - - private class SuggestionLoaderCallbacks implements LoaderManager.LoaderCallbacks<List<Suggestion>> { - @Override - public Loader<List<Suggestion>> onCreateLoader(int id, Bundle args) { - // We drop the user's search if suggestclient isn't ready. This happens if the - // user is really fast and starts typing before we can read shared prefs. - if (suggestClient != null) { - return new SuggestionAsyncLoader(getActivity(), suggestClient, args.getString(KEY_SEARCH_TERM)); - } - Log.e(LOG_TAG, "Autocomplete setup failed; suggestClient not ready yet."); - return null; - } - - @Override - public void onLoadFinished(Loader<List<Suggestion>> loader, List<Suggestion> suggestions) { - // Only show the ListView if there are suggestions in it. - if (suggestions.size() > 0) { - autoCompleteAdapter.update(suggestions); - suggestionsList.setVisibility(View.VISIBLE); - } else { - suggestionsList.setVisibility(View.INVISIBLE); - } - } - - @Override - public void onLoaderReset(Loader<List<Suggestion>> loader) { } - } - - private static class SuggestionAsyncLoader extends AsyncTaskLoader<List<Suggestion>> { - private final SuggestClient suggestClient; - private final String searchTerm; - private List<Suggestion> suggestions; - private final int suggestionHighlightColor; - - public SuggestionAsyncLoader(Context context, SuggestClient suggestClient, String searchTerm) { - super(context); - this.suggestClient = suggestClient; - this.searchTerm = searchTerm; - this.suggestions = null; - - // Color of search term match in search suggestion - suggestionHighlightColor = ContextCompat.getColor(context, R.color.suggestion_highlight); - } - - @Override - public List<Suggestion> loadInBackground() { - final List<String> values = suggestClient.query(searchTerm); - - final List<Suggestion> result = new ArrayList<Suggestion>(values.size()); - for (String value : values) { - result.add(new Suggestion(value, searchTerm, suggestionHighlightColor)); - } - - return result; - } - - @Override - public void deliverResult(List<Suggestion> suggestions) { - this.suggestions = suggestions; - - if (isStarted()) { - super.deliverResult(suggestions); - } - } - - @Override - protected void onStartLoading() { - if (suggestions != null) { - deliverResult(suggestions); - } - - if (takeContentChanged() || suggestions == null) { - forceLoad(); - } - } - - @Override - protected void onStopLoading() { - cancelLoad(); - } - - @Override - protected void onReset() { - super.onReset(); - - onStopLoading(); - suggestions = null; - } - } -} diff --git a/mobile/android/search/java/org/mozilla/search/ui/BackCaptureEditText.java b/mobile/android/search/java/org/mozilla/search/ui/BackCaptureEditText.java deleted file mode 100644 index 727ad8105..000000000 --- a/mobile/android/search/java/org/mozilla/search/ui/BackCaptureEditText.java +++ /dev/null @@ -1,36 +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/. */ - -package org.mozilla.search.ui; - -import android.content.Context; -import android.util.AttributeSet; -import android.view.KeyEvent; -import android.widget.EditText; - -/** - * An EditText subclass that loses focus when the keyboard - * is dismissed. - */ -public class BackCaptureEditText extends EditText { - public BackCaptureEditText(Context context) { - super(context); - } - - public BackCaptureEditText(Context context, AttributeSet attrs) { - super(context, attrs); - } - - public BackCaptureEditText(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - } - - @Override - public boolean onKeyPreIme(int keyCode, KeyEvent event) { - if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) { - clearFocus(); - } - return super.onKeyPreIme(keyCode, event); - } -} diff --git a/mobile/android/search/java/org/mozilla/search/ui/FacetBar.java b/mobile/android/search/java/org/mozilla/search/ui/FacetBar.java deleted file mode 100644 index 7fcf3dc9b..000000000 --- a/mobile/android/search/java/org/mozilla/search/ui/FacetBar.java +++ /dev/null @@ -1,124 +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/. */ - -package org.mozilla.search.ui; - -import org.mozilla.gecko.R; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.util.AttributeSet; -import android.widget.RadioButton; -import android.widget.RadioGroup; - -public class FacetBar extends RadioGroup { - - // Ensure facets have equal width and match the bar's height. Supplying these - // in styles.xml/FacetButtonStyle does not work. See: - // http://stackoverflow.com/questions/24213193/android-ignores-layout-weight-parameter-from-styles-xml - private static final RadioGroup.LayoutParams FACET_LAYOUT_PARAMS = - new RadioGroup.LayoutParams(0, LayoutParams.MATCH_PARENT, 1.0f); - - // A loud default color to make it obvious that setUnderlineColor should be called. - private int underlineColor = Color.RED; - - // Used for assigning unique view ids when facet buttons are being created. - private int nextButtonId = 0; - - public FacetBar(Context context, AttributeSet attrs) { - super(context, attrs); - } - - /** - * Add a new button to the facet bar. - * - * @param facetName The text to be used in the button. - */ - public void addFacet(String facetName) { - addFacet(facetName, false); - } - - /** - * Add a new button to the facet bar. - * - * @param facetName The text to be used in the button. - * @param checked Whether the button should be checked. If true, the - * onCheckChange listener *will* be fired. - */ - public void addFacet(String facetName, boolean checked) { - final FacetButton button = new FacetButton(getContext(), facetName, underlineColor); - - // The ids are used internally by RadioGroup to manage which button is - // currently checked. Since we are programmatically creating the buttons, - // we need to manually assign an id. - button.setId(nextButtonId++); - - // Ensure the buttons are equally spaced. - button.setLayoutParams(FACET_LAYOUT_PARAMS); - - // If true, this will fire the onCheckChange listener. - button.setChecked(checked); - - addView(button); - } - - /** - * Update the brand color for all of the buttons. - */ - public void setUnderlineColor(int underlineColor) { - this.underlineColor = underlineColor; - - if (getChildCount() > 0) { - for (int i = 0; i < getChildCount(); i++) { - ((FacetButton) getChildAt(i)).setUnderlineColor(underlineColor); - } - } - } - - /** - * A custom TextView that includes a bottom border. The bottom border - * can have a custom color and thickness. - */ - private static class FacetButton extends RadioButton { - - private final Paint underlinePaint = new Paint(); - - public FacetButton(Context context, String text, int color) { - super(context, null, R.attr.facetButtonStyle); - - setText(text); - - underlinePaint.setStyle(Paint.Style.STROKE); - underlinePaint.setStrokeWidth(getResources().getDimension(R.dimen.facet_button_underline_thickness)); - underlinePaint.setColor(color); - } - - @Override - public void setChecked(boolean checked) { - super.setChecked(checked); - - // Force the button to redraw to update the underline state. - invalidate(); - } - - @Override - protected void onDraw(Canvas canvas) { - super.onDraw(canvas); - if (isChecked()) { - // Translate the line upward so that it isn't clipped by the button's boundary. - // We divide by 2 since, without offset, the line would be drawn with its - // midpoint at the bottom of the button -- half of the stroke going up, - // and half of the stroke getting clipped. - final float yPos = getHeight() - underlinePaint.getStrokeWidth() / 2; - canvas.drawLine(0, yPos, getWidth(), yPos, underlinePaint); - } - } - - public void setUnderlineColor(int color) { - underlinePaint.setColor(color); - } - } -} |