GoogleSuggestClient.java revision 516781305d7427e79928c95c6ea2b7689a4bf6ce
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.android.quicksearchbox.google; 18 19import com.android.quicksearchbox.R; 20import com.android.quicksearchbox.Source; 21import com.android.quicksearchbox.SourceResult; 22import com.android.quicksearchbox.SuggestionCursor; 23import com.android.quicksearchbox.util.NamedTaskExecutor; 24 25import org.apache.http.HttpResponse; 26import org.apache.http.client.HttpClient; 27import org.apache.http.client.methods.HttpGet; 28import org.apache.http.impl.client.DefaultHttpClient; 29import org.apache.http.params.HttpParams; 30import org.apache.http.params.HttpProtocolParams; 31import org.apache.http.util.EntityUtils; 32import org.json.JSONArray; 33import org.json.JSONException; 34 35import android.content.ComponentName; 36import android.content.Context; 37import android.net.ConnectivityManager; 38import android.net.NetworkInfo; 39import android.os.Handler; 40import android.text.TextUtils; 41import android.util.Log; 42 43import java.io.IOException; 44import java.io.UnsupportedEncodingException; 45import java.net.URLEncoder; 46import java.util.Locale; 47 48/** 49 * Use network-based Google Suggests to provide search suggestions. 50 */ 51public class GoogleSuggestClient extends AbstractGoogleSource { 52 53 private static final boolean DBG = false; 54 private static final String LOG_TAG = "GoogleSearch"; 55 56 private static final String USER_AGENT = "Android/1.0"; 57 private String mSuggestUri; 58 private static final int HTTP_TIMEOUT_MS = 1000; 59 60 // TODO: this should be defined somewhere 61 private static final String HTTP_TIMEOUT = "http.connection-manager.timeout"; 62 63 private final HttpClient mHttpClient; 64 65 public GoogleSuggestClient(Context context, Handler uiThread, NamedTaskExecutor iconLoader) { 66 super(context, uiThread, iconLoader); 67 mHttpClient = new DefaultHttpClient(); 68 HttpParams params = mHttpClient.getParams(); 69 HttpProtocolParams.setUserAgent(params, USER_AGENT); 70 params.setLongParameter(HTTP_TIMEOUT, HTTP_TIMEOUT_MS); 71 72 // NOTE: Do not look up the resource here; Localization changes may not have completed 73 // yet (e.g. we may still be reading the SIM card). 74 mSuggestUri = null; 75 } 76 77 @Override 78 public ComponentName getIntentComponent() { 79 return new ComponentName(getContext(), GoogleSearch.class); 80 } 81 82 @Override 83 public SourceResult queryInternal(String query) { 84 return query(query); 85 } 86 87 @Override 88 public SourceResult queryExternal(String query) { 89 return query(query); 90 } 91 92 /** 93 * Queries for a given search term and returns a cursor containing 94 * suggestions ordered by best match. 95 */ 96 private SourceResult query(String query) { 97 if (TextUtils.isEmpty(query)) { 98 return null; 99 } 100 if (!isNetworkConnected()) { 101 Log.i(LOG_TAG, "Not connected to network."); 102 return null; 103 } 104 try { 105 query = URLEncoder.encode(query, "UTF-8"); 106 if (mSuggestUri == null) { 107 Locale l = Locale.getDefault(); 108 String language = GoogleSearch.getLanguage(l); 109 mSuggestUri = getContext().getResources().getString(R.string.google_suggest_base, 110 language) 111 + "json=true&q="; 112 } 113 114 String suggestUri = mSuggestUri + query; 115 if (DBG) Log.d(LOG_TAG, "Sending request: " + suggestUri); 116 HttpGet method = new HttpGet(suggestUri); 117 HttpResponse response = mHttpClient.execute(method); 118 if (response.getStatusLine().getStatusCode() == 200) { 119 120 /* Goto http://www.google.com/complete/search?json=true&q=foo 121 * to see what the data format looks like. It's basically a json 122 * array containing 4 other arrays. We only care about the middle 123 * 2 which contain the suggestions and their popularity. 124 */ 125 JSONArray results = new JSONArray(EntityUtils.toString(response.getEntity())); 126 JSONArray suggestions = results.getJSONArray(1); 127 JSONArray popularity = results.getJSONArray(2); 128 if (DBG) Log.d(LOG_TAG, "Got " + suggestions.length() + " results"); 129 return new GoogleSuggestCursor(this, query, suggestions, popularity); 130 } else { 131 if (DBG) Log.d(LOG_TAG, "Request failed " + response.getStatusLine()); 132 } 133 } catch (UnsupportedEncodingException e) { 134 Log.w(LOG_TAG, "Error", e); 135 } catch (IOException e) { 136 Log.w(LOG_TAG, "Error", e); 137 } catch (JSONException e) { 138 Log.w(LOG_TAG, "Error", e); 139 } 140 return null; 141 } 142 143 @Override 144 public SuggestionCursor refreshShortcut(String shortcutId, String oldExtraData) { 145 return null; 146 } 147 148 private boolean isNetworkConnected() { 149 NetworkInfo networkInfo = getActiveNetworkInfo(); 150 return networkInfo != null && networkInfo.isConnected(); 151 } 152 153 private NetworkInfo getActiveNetworkInfo() { 154 ConnectivityManager connectivity = 155 (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE); 156 if (connectivity == null) { 157 return null; 158 } 159 return connectivity.getActiveNetworkInfo(); 160 } 161 162 private static class GoogleSuggestCursor extends AbstractGoogleSourceResult { 163 164 /* Contains the actual suggestions */ 165 private final JSONArray mSuggestions; 166 167 /* This contains the popularity of each suggestion 168 * i.e. 165,000 results. It's not related to sorting. 169 */ 170 private final JSONArray mPopularity; 171 172 public GoogleSuggestCursor(Source source, String userQuery, 173 JSONArray suggestions, JSONArray popularity) { 174 super(source, userQuery); 175 mSuggestions = suggestions; 176 mPopularity = popularity; 177 } 178 179 @Override 180 public int getCount() { 181 return mSuggestions.length(); 182 } 183 184 @Override 185 public String getSuggestionQuery() { 186 try { 187 return mSuggestions.getString(getPosition()); 188 } catch (JSONException e) { 189 Log.w(LOG_TAG, "Error parsing response: " + e); 190 return null; 191 } 192 } 193 194 @Override 195 public String getSuggestionText2() { 196 try { 197 return mPopularity.getString(getPosition()); 198 } catch (JSONException e) { 199 Log.w(LOG_TAG, "Error parsing response: " + e); 200 return null; 201 } 202 } 203 } 204} 205