michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this file, michael@0: * You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: package org.mozilla.gecko.home; michael@0: michael@0: import org.mozilla.gecko.GeckoAppShell; michael@0: import org.mozilla.gecko.mozglue.RobocopTarget; michael@0: michael@0: import org.json.JSONArray; michael@0: michael@0: import android.content.Context; michael@0: import android.net.ConnectivityManager; michael@0: import android.net.NetworkInfo; michael@0: import android.text.TextUtils; michael@0: import android.util.Log; michael@0: michael@0: import java.io.BufferedInputStream; michael@0: import java.io.IOException; michael@0: import java.io.InputStream; michael@0: import java.net.HttpURLConnection; michael@0: import java.net.URL; michael@0: import java.net.URLEncoder; michael@0: import java.util.ArrayList; michael@0: michael@0: /** michael@0: * Use network-based search suggestions. michael@0: */ michael@0: public class SuggestClient { michael@0: private static final String LOGTAG = "GeckoSuggestClient"; michael@0: private static final String USER_AGENT = GeckoAppShell.getGeckoInterface().getDefaultUAString(); michael@0: michael@0: private final Context mContext; michael@0: private final int mTimeout; michael@0: michael@0: // should contain the string "__searchTerms__", which is replaced with the query michael@0: private final String mSuggestTemplate; michael@0: michael@0: // the maximum number of suggestions to return michael@0: private final int mMaxResults; michael@0: michael@0: // used by robocop for testing michael@0: private boolean mCheckNetwork; michael@0: michael@0: // used to make suggestions appear instantly after opt-in michael@0: private String mPrevQuery; michael@0: private ArrayList mPrevResults; michael@0: michael@0: public SuggestClient(Context context, String suggestTemplate, int timeout, int maxResults) { michael@0: mContext = context; michael@0: mMaxResults = maxResults; michael@0: mSuggestTemplate = suggestTemplate; michael@0: mTimeout = timeout; michael@0: mCheckNetwork = true; michael@0: } michael@0: michael@0: /** michael@0: * This constructor is used exclusively by Robocop. michael@0: */ michael@0: @RobocopTarget michael@0: public SuggestClient(Context context, String suggestTemplate, int timeout) { michael@0: this(context, suggestTemplate, timeout, Integer.MAX_VALUE); michael@0: mCheckNetwork = false; michael@0: } michael@0: michael@0: /** michael@0: * Queries for a given search term and returns an ArrayList of suggestions. michael@0: */ michael@0: public ArrayList query(String query) { michael@0: if (query.equals(mPrevQuery)) michael@0: return mPrevResults; michael@0: michael@0: ArrayList suggestions = new ArrayList(); michael@0: if (TextUtils.isEmpty(mSuggestTemplate) || TextUtils.isEmpty(query)) { michael@0: return suggestions; michael@0: } michael@0: michael@0: if (!isNetworkConnected() && mCheckNetwork) { michael@0: Log.i(LOGTAG, "Not connected to network"); michael@0: return suggestions; michael@0: } michael@0: michael@0: try { michael@0: String encoded = URLEncoder.encode(query, "UTF-8"); michael@0: String suggestUri = mSuggestTemplate.replace("__searchTerms__", encoded); michael@0: michael@0: URL url = new URL(suggestUri); michael@0: String json = null; michael@0: HttpURLConnection urlConnection = null; michael@0: InputStream in = null; michael@0: try { michael@0: urlConnection = (HttpURLConnection) url.openConnection(); michael@0: urlConnection.setConnectTimeout(mTimeout); michael@0: urlConnection.setRequestProperty("User-Agent", USER_AGENT); michael@0: in = new BufferedInputStream(urlConnection.getInputStream()); michael@0: json = convertStreamToString(in); michael@0: } finally { michael@0: if (urlConnection != null) michael@0: urlConnection.disconnect(); michael@0: if (in != null) { michael@0: try { michael@0: in.close(); michael@0: } catch (IOException e) { michael@0: Log.e(LOGTAG, "error", e); michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (json != null) { michael@0: /* michael@0: * Sample result: michael@0: * ["foo",["food network","foothill college","foot locker",...]] michael@0: */ michael@0: JSONArray results = new JSONArray(json); michael@0: JSONArray jsonSuggestions = results.getJSONArray(1); michael@0: michael@0: int added = 0; michael@0: for (int i = 0; (i < jsonSuggestions.length()) && (added < mMaxResults); i++) { michael@0: String suggestion = jsonSuggestions.getString(i); michael@0: if (!suggestion.equalsIgnoreCase(query)) { michael@0: suggestions.add(suggestion); michael@0: added++; michael@0: } michael@0: } michael@0: } else { michael@0: Log.e(LOGTAG, "Suggestion query failed"); michael@0: } michael@0: } catch (Exception e) { michael@0: Log.e(LOGTAG, "Error", e); michael@0: } michael@0: michael@0: mPrevQuery = query; michael@0: mPrevResults = suggestions; michael@0: return suggestions; michael@0: } michael@0: michael@0: private boolean isNetworkConnected() { michael@0: NetworkInfo networkInfo = getActiveNetworkInfo(); michael@0: return networkInfo != null && networkInfo.isConnected(); michael@0: } michael@0: michael@0: private NetworkInfo getActiveNetworkInfo() { michael@0: ConnectivityManager connectivity = (ConnectivityManager) mContext michael@0: .getSystemService(Context.CONNECTIVITY_SERVICE); michael@0: if (connectivity == null) michael@0: return null; michael@0: return connectivity.getActiveNetworkInfo(); michael@0: } michael@0: michael@0: private String convertStreamToString(java.io.InputStream is) { michael@0: try { michael@0: return new java.util.Scanner(is).useDelimiter("\\A").next(); michael@0: } catch (java.util.NoSuchElementException e) { michael@0: return ""; michael@0: } michael@0: } michael@0: }