• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.quicksearchbox.google;
18 
19 import com.android.quicksearchbox.Config;
20 import com.android.quicksearchbox.R;
21 import com.android.quicksearchbox.Source;
22 import com.android.quicksearchbox.SourceResult;
23 import com.android.quicksearchbox.SuggestionCursor;
24 import com.android.quicksearchbox.util.NamedTaskExecutor;
25 
26 import org.apache.http.HttpResponse;
27 import org.apache.http.client.HttpClient;
28 import org.apache.http.client.methods.HttpGet;
29 import org.apache.http.params.HttpParams;
30 import org.apache.http.params.HttpProtocolParams;
31 import org.apache.http.util.EntityUtils;
32 import org.json.JSONArray;
33 import org.json.JSONException;
34 
35 import android.content.ComponentName;
36 import android.content.Context;
37 import android.net.ConnectivityManager;
38 import android.net.http.AndroidHttpClient;
39 import android.net.NetworkInfo;
40 import android.os.Build;
41 import android.os.Handler;
42 import android.text.TextUtils;
43 import android.util.Log;
44 
45 import java.io.IOException;
46 import java.io.UnsupportedEncodingException;
47 import java.net.URLEncoder;
48 import java.util.Locale;
49 
50 /**
51  * Use network-based Google Suggests to provide search suggestions.
52  */
53 public class GoogleSuggestClient extends AbstractGoogleSource {
54 
55     private static final boolean DBG = false;
56     private static final String LOG_TAG = "GoogleSearch";
57 
58     private static final String USER_AGENT = "Android/" + Build.VERSION.RELEASE;
59     private String mSuggestUri;
60 
61     // TODO: this should be defined somewhere
62     private static final String HTTP_TIMEOUT = "http.conn-manager.timeout";
63 
64     private final HttpClient mHttpClient;
65 
GoogleSuggestClient(Context context, Handler uiThread, NamedTaskExecutor iconLoader, Config config)66     public GoogleSuggestClient(Context context, Handler uiThread,
67             NamedTaskExecutor iconLoader, Config config) {
68         super(context, uiThread, iconLoader);
69         mHttpClient = AndroidHttpClient.newInstance(USER_AGENT, context);
70         HttpParams params = mHttpClient.getParams();
71         params.setLongParameter(HTTP_TIMEOUT, config.getHttpConnectTimeout());
72 
73         // NOTE:  Do not look up the resource here;  Localization changes may not have completed
74         // yet (e.g. we may still be reading the SIM card).
75         mSuggestUri = null;
76     }
77 
78     @Override
getIntentComponent()79     public ComponentName getIntentComponent() {
80         return new ComponentName(getContext(), GoogleSearch.class);
81     }
82 
83     @Override
queryInternal(String query)84     public SourceResult queryInternal(String query) {
85         return query(query);
86     }
87 
88     @Override
queryExternal(String query)89     public SourceResult queryExternal(String query) {
90         return query(query);
91     }
92 
93     /**
94      * Queries for a given search term and returns a cursor containing
95      * suggestions ordered by best match.
96      */
query(String query)97     private SourceResult query(String query) {
98         if (TextUtils.isEmpty(query)) {
99             return null;
100         }
101         if (!isNetworkConnected()) {
102             Log.i(LOG_TAG, "Not connected to network.");
103             return null;
104         }
105         try {
106             query = URLEncoder.encode(query, "UTF-8");
107             if (mSuggestUri == null) {
108                 Locale l = Locale.getDefault();
109                 String language = GoogleSearch.getLanguage(l);
110                 mSuggestUri = getContext().getResources().getString(R.string.google_suggest_base,
111                                                                     language);
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
refreshShortcut(String shortcutId, String oldExtraData)144     public SuggestionCursor refreshShortcut(String shortcutId, String oldExtraData) {
145         return null;
146     }
147 
isNetworkConnected()148     private boolean isNetworkConnected() {
149         NetworkInfo networkInfo = getActiveNetworkInfo();
150         return networkInfo != null && networkInfo.isConnected();
151     }
152 
getActiveNetworkInfo()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 
GoogleSuggestCursor(Source source, String userQuery, JSONArray suggestions, JSONArray popularity)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
getCount()180         public int getCount() {
181             return mSuggestions.length();
182         }
183 
184         @Override
getSuggestionQuery()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
getSuggestionText2()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