Java tutorial
/* * Copyright (C) 2008 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.quicksearchbox.google; import com.android.quicksearchbox.R; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.params.HttpParams; import org.apache.http.util.EntityUtils; import org.json.JSONArray; import org.json.JSONException; import android.app.SearchManager; import android.content.ContentProvider; import android.content.ContentValues; import android.content.Context; import android.database.AbstractCursor; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.net.http.AndroidHttpClient; import android.text.TextUtils; import android.util.Log; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Locale; /** * Use network-based Google Suggests to provide search suggestions. * * Future: Merge live suggestions with saved recent queries */ public class GoogleSuggestionProvider extends ContentProvider { private static final boolean DBG = false; private static final String LOG_TAG = "GoogleSearch"; private static final String USER_AGENT = "Android/1.0"; private String mSuggestUri; private static final int HTTP_TIMEOUT_MS = 1000; // TODO: this should be defined somewhere private static final String HTTP_TIMEOUT = "http.connection-manager.timeout"; // Indexes into COLUMNS private static final int COL_ID = 0; private static final int COL_TEXT_1 = 1; private static final int COL_TEXT_2 = 2; private static final int COL_ICON_1 = 3; private static final int COL_ICON_2 = 4; private static final int COL_QUERY = 5; /* The suggestion columns used */ private static final String[] COLUMNS = new String[] { "_id", SearchManager.SUGGEST_COLUMN_TEXT_1, SearchManager.SUGGEST_COLUMN_TEXT_2, SearchManager.SUGGEST_COLUMN_ICON_1, SearchManager.SUGGEST_COLUMN_ICON_2, SearchManager.SUGGEST_COLUMN_QUERY }; private HttpClient mHttpClient; @Override public boolean onCreate() { mHttpClient = AndroidHttpClient.newInstance(USER_AGENT, getContext()); HttpParams params = mHttpClient.getParams(); params.setLongParameter(HTTP_TIMEOUT, HTTP_TIMEOUT_MS); // NOTE: Do not look up the resource here; Localization changes may not have completed // yet (e.g. we may still be reading the SIM card). mSuggestUri = null; return true; } /** * This will always return {@link SearchManager#SUGGEST_MIME_TYPE} as this * provider is purely to provide suggestions. */ @Override public String getType(Uri uri) { return SearchManager.SUGGEST_MIME_TYPE; } /** * Queries for a given search term and returns a cursor containing * suggestions ordered by best match. */ @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { String query = getQuery(uri); if (TextUtils.isEmpty(query)) { return null; } if (!isNetworkConnected()) { Log.i(LOG_TAG, "Not connected to network."); return null; } try { query = URLEncoder.encode(query, "UTF-8"); // NOTE: This code uses resources to optionally select the search Uri, based on the // MCC value from the SIM. iThe default string will most likely be fine. It is // paramerterized to accept info from the Locale, the language code is the first // parameter (%1$s) and the country code is the second (%2$s). This code *must* // function in the same way as a similar lookup in // com.android.browser.BrowserActivity#onCreate(). If you change // either of these functions, change them both. (The same is true for the underlying // resource strings, which are stored in mcc-specific xml files.) if (mSuggestUri == null) { Locale l = Locale.getDefault(); String language = l.getLanguage(); String country = l.getCountry().toLowerCase(); // Chinese and Portuguese have two langauge variants. if ("zh".equals(language)) { if ("cn".equals(country)) { language = "zh-CN"; } else if ("tw".equals(country)) { language = "zh-TW"; } } else if ("pt".equals(language)) { if ("br".equals(country)) { language = "pt-BR"; } else if ("pt".equals(country)) { language = "pt-PT"; } } mSuggestUri = getContext().getResources().getString(R.string.google_suggest_base, language, country) + "json=true&q="; } String suggestUri = mSuggestUri + query; if (DBG) Log.d(LOG_TAG, "Sending request: " + suggestUri); HttpGet method = new HttpGet(suggestUri); HttpResponse response = mHttpClient.execute(method); if (response.getStatusLine().getStatusCode() == 200) { /* Goto http://www.google.com/complete/search?json=true&q=foo * to see what the data format looks like. It's basically a json * array containing 4 other arrays. We only care about the middle * 2 which contain the suggestions and their popularity. */ JSONArray results = new JSONArray(EntityUtils.toString(response.getEntity())); JSONArray suggestions = results.getJSONArray(1); JSONArray popularity = results.getJSONArray(2); if (DBG) Log.d(LOG_TAG, "Got " + suggestions.length() + " results"); return new SuggestionsCursor(suggestions, popularity); } else { if (DBG) Log.d(LOG_TAG, "Request failed " + response.getStatusLine()); } } catch (UnsupportedEncodingException e) { Log.w(LOG_TAG, "Error", e); } catch (IOException e) { Log.w(LOG_TAG, "Error", e); } catch (JSONException e) { Log.w(LOG_TAG, "Error", e); } return null; } /** * Gets the search text from a uri. */ private String getQuery(Uri uri) { if (uri.getPathSegments().size() > 1) { return uri.getLastPathSegment(); } else { return ""; } } private boolean isNetworkConnected() { NetworkInfo networkInfo = getActiveNetworkInfo(); return networkInfo != null && networkInfo.isConnected(); } private NetworkInfo getActiveNetworkInfo() { ConnectivityManager connectivity = (ConnectivityManager) getContext() .getSystemService(Context.CONNECTIVITY_SERVICE); if (connectivity == null) { return null; } return connectivity.getActiveNetworkInfo(); } private static class SuggestionsCursor extends AbstractCursor { /* Contains the actual suggestions */ final JSONArray mSuggestions; /* This contains the popularity of each suggestion * i.e. 165,000 results. It's not related to sorting. */ final JSONArray mPopularity; public SuggestionsCursor(JSONArray suggestions, JSONArray popularity) { mSuggestions = suggestions; mPopularity = popularity; } @Override public int getCount() { return mSuggestions.length(); } @Override public String[] getColumnNames() { return COLUMNS; } @Override public String getString(int column) { if (mPos == -1) return null; try { switch (column) { case COL_ID: return String.valueOf(mPos); case COL_TEXT_1: case COL_QUERY: return mSuggestions.getString(mPos); case COL_TEXT_2: return mPopularity.getString(mPos); case COL_ICON_1: return String.valueOf(R.drawable.magnifying_glass); case COL_ICON_2: return null; default: Log.w(LOG_TAG, "Bad column: " + column); return null; } } catch (JSONException e) { Log.w(LOG_TAG, "Error parsing response: " + e); return null; } } @Override public double getDouble(int column) { throw new UnsupportedOperationException(); } @Override public float getFloat(int column) { throw new UnsupportedOperationException(); } @Override public int getInt(int column) { throw new UnsupportedOperationException(); } @Override public long getLong(int column) { if (column == COL_ID) { return mPos; // use row# as the _Id } throw new UnsupportedOperationException(); } @Override public short getShort(int column) { throw new UnsupportedOperationException(); } @Override public boolean isNull(int column) { throw new UnsupportedOperationException(); } } @Override public Uri insert(Uri uri, ContentValues values) { throw new UnsupportedOperationException(); } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { throw new UnsupportedOperationException(); } }