summaryrefslogtreecommitdiff
path: root/src/com/android
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/googlesearch/GoogleSearch.java120
-rw-r--r--src/com/android/googlesearch/LocationUtils.java104
-rw-r--r--src/com/android/googlesearch/SuggestionProvider.java82
3 files changed, 270 insertions, 36 deletions
diff --git a/src/com/android/googlesearch/GoogleSearch.java b/src/com/android/googlesearch/GoogleSearch.java
index 63a870d..e08f2b1 100644
--- a/src/com/android/googlesearch/GoogleSearch.java
+++ b/src/com/android/googlesearch/GoogleSearch.java
@@ -16,37 +16,131 @@
package com.android.googlesearch;
+import com.google.android.providers.GoogleSettings.Partner;
+
+import java.io.UnsupportedEncodingException;
+import java.net.URLEncoder;
+import java.util.Locale;
+
import android.app.Activity;
import android.app.SearchManager;
+import android.content.ContentResolver;
+import android.content.Context;
import android.content.Intent;
+import android.location.Location;
+import android.location.LocationManager;
+import android.net.Uri;
import android.os.Bundle;
-import android.provider.SearchRecentSuggestions;
+import android.provider.Browser;
+import android.provider.Settings;
import android.text.TextUtils;
+import android.util.Log;
/**
* This class is purely here to get search queries and route them to
* the global {@link Intent#ACTION_WEB_SEARCH}.
*/
public class GoogleSearch extends Activity {
+ private static final String TAG = "GoogleSearch";
+
+ // The template URL we should use to format google search requests.
+ private String googleSearchUrlBase = null;
+
+ // "source" parameter for Google search requests from unknown sources (e.g. apps). This will get
+ // prefixed with the string 'android-' before being sent on the wire.
+ final static String GOOGLE_SEARCH_SOURCE_UNKNOWN = "unknown";
+ private LocationUtils mLocationUtils;
+
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ mLocationUtils = LocationUtils.getLocationUtils(this);
Intent intent = getIntent();
- if ((intent != null) && Intent.ACTION_SEARCH.equals(intent.getAction())) {
- String query = intent.getStringExtra(SearchManager.QUERY);
- if (!TextUtils.isEmpty(query)) {
- // forward query to browser for Google search
- Intent search = new Intent(Intent.ACTION_WEB_SEARCH);
- search.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- search.putExtra(SearchManager.QUERY, query);
- final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
- if (appData != null) {
- search.putExtra(SearchManager.APP_DATA, appData);
+ if ((intent != null) && Intent.ACTION_WEB_SEARCH.equals(intent.getAction())) {
+ handleWebSearchIntent(intent);
+ }
+ finish();
+ }
+
+ /**
+ * NOTE: This function is similar to the one found in
+ * com.google.android.providers.enhancedgooglesearch.Launcher. If you are changing this
+ * make sure you change both.
+ */
+ private void handleWebSearchIntent(Intent intent) {
+ String query = intent.getStringExtra(SearchManager.QUERY);
+ if (TextUtils.isEmpty(query)) {
+ Log.w(TAG, "Got search intent with no query.");
+ return;
+ }
+
+ if (googleSearchUrlBase == 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";
}
- startActivity(search);
}
+ googleSearchUrlBase = getResources().getString(
+ R.string.google_search_base, language, country)
+ + "client=ms-"
+ + Partner.getString(this.getContentResolver(), Partner.CLIENT_ID);
}
- finish();
+
+ // If the caller specified a 'source' url parameter, use that and if not use default.
+ Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA);
+ String source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
+ if (appSearchData != null) {
+ source = appSearchData.getString(SearchManager.SOURCE);
+ }
+
+ try {
+ String searchUri = googleSearchUrlBase
+ + "&source=android-" + source
+ + "&q=" + URLEncoder.encode(query, "UTF-8");
+ Intent launchUriIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri));
+ launchUriIntent.putExtra(Browser.EXTRA_POST_DATA, getLocationData());
+ launchUriIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(launchUriIntent);
+ } catch (UnsupportedEncodingException e) {
+ Log.w(TAG, "Error", e);
+ }
+ }
+
+ private byte[] getLocationData() {
+ byte[] postData = null;
+ ContentResolver cr = getContentResolver();
+
+ // Don't send any location if the system does not have GoogleSettingsProvider.
+ if (!mLocationUtils.systemHasGoogleSettingsProvider()) return postData;
+
+ if (!mLocationUtils.userRespondedToLocationOptIn()) {
+ // Bring up the consent dialog if it the user has yet responded to it. We
+ // will not send the location info for this query.
+ mLocationUtils.showLocationOptIn();
+ } else if (mLocationUtils.userAcceptedLocationOptIn() &&
+ Settings.Secure.isLocationProviderEnabled(cr, LocationManager.NETWORK_PROVIDER)) {
+ Location location = ((LocationManager) getSystemService(
+ Context.LOCATION_SERVICE)).getLastKnownLocation(
+ LocationManager.NETWORK_PROVIDER);
+ if (location != null) {
+ StringBuilder str = new StringBuilder("action=devloc&sll=");
+ str.append(location.getLatitude()).append(',').append(location.getLongitude());
+ postData = str.toString().getBytes();
+ }
+ }
+ return postData;
}
}
diff --git a/src/com/android/googlesearch/LocationUtils.java b/src/com/android/googlesearch/LocationUtils.java
new file mode 100644
index 0000000..e684776
--- /dev/null
+++ b/src/com/android/googlesearch/LocationUtils.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009 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.googlesearch;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+
+/**
+ * Utility methods for dealing with location (such as opt-in stuff).
+ */
+public class LocationUtils {
+ private Context mContext;
+
+ // The singleton object.
+ private static LocationUtils sLocationUtils;
+
+ /**
+ * Gets the singleton.
+ */
+ public static synchronized LocationUtils getLocationUtils(Context context) {
+ if (sLocationUtils == null) {
+ sLocationUtils = new LocationUtils(context);
+ }
+ return sLocationUtils;
+ }
+
+ /**
+ * Private constructor for singleton class; use {@link #getLocationUtils(Context)}.
+ */
+ private LocationUtils(Context context) {
+ mContext = context;
+ }
+
+ /**
+ * Identifies whether this system has the GoogleSettingsProvider, which determines
+ * whether the other methods in this class are relevant, or if we should just avoid
+ * using location.
+ */
+ public boolean systemHasGoogleSettingsProvider() {
+ try {
+ return mContext.getPackageManager().getPackageInfo(
+ "com.google.android.providers.settings", 0) != null;
+ } catch (PackageManager.NameNotFoundException e) {
+ return false;
+ }
+ }
+
+ /**
+ * Checks whether the user has responded (either positively or negatively) to the
+ * Google location opt-in.
+ */
+ public boolean userRespondedToLocationOptIn() {
+ return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.USE_LOCATION_FOR_SERVICES, 2) != 2;
+ }
+
+ /**
+ * Shows the location opt-in because the user has not yet responded to it. If
+ * we have GoogleSettingsProvider, this fires up the 'security & location' settings
+ * and requests to show the opt-in. If we do not, this does nothing.
+ */
+ public void showLocationOptIn() {
+ if (systemHasGoogleSettingsProvider()) {
+ Intent consent = new Intent(
+ android.provider.Settings.ACTION_SECURITY_SETTINGS);
+ consent.putExtra("SHOW_USE_LOCATION", true);
+ consent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(consent);
+ }
+ }
+
+ /**
+ * Indicates whether the user has accepted the Google location opt-in. Checks the appropriate
+ * setting depending on whether we are using the GoogleSettingsProvider setting or our
+ * own package-local setting.
+ *
+ * If the answer is false, it could be because the user responded negatively to the opt-in,
+ * or because the system does not have GoogleSettingsProvider. Use
+ * {@link #userRespondedToLocationOptIn()} to distinguish between these two cases.
+ */
+ public boolean userAcceptedLocationOptIn() {
+ if (systemHasGoogleSettingsProvider()) {
+ return android.provider.Settings.Secure.getInt(mContext.getContentResolver(),
+ android.provider.Settings.Secure.USE_LOCATION_FOR_SERVICES, 2) == 1;
+ } else {
+ return false;
+ }
+ }
+}
diff --git a/src/com/android/googlesearch/SuggestionProvider.java b/src/com/android/googlesearch/SuggestionProvider.java
index 416f9d0..c6ccfb1 100644
--- a/src/com/android/googlesearch/SuggestionProvider.java
+++ b/src/com/android/googlesearch/SuggestionProvider.java
@@ -31,8 +31,12 @@ import org.json.JSONException;
import android.app.SearchManager;
import android.content.ContentProvider;
import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
import android.database.AbstractCursor;
import android.database.Cursor;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
@@ -45,29 +49,31 @@ import java.util.Locale;
/**
* Use network-based Google Suggests to provide search suggestions.
- *
+ *
* Future: Merge live suggestions with saved recent queries
*/
public class SuggestionProvider extends ContentProvider {
-
+
public static final Uri CONTENT_URI = Uri.parse(
"content://com.android.googlesearch.SuggestionProvider");
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";
private static final String LOG_TAG = "GoogleSearch.SuggestionProvider";
-
+
/* 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_QUERY};
+ "_id",
+ SearchManager.SUGGEST_COLUMN_TEXT_1,
+ SearchManager.SUGGEST_COLUMN_TEXT_2,
+ SearchManager.SUGGEST_COLUMN_QUERY,
+ SearchManager.SUGGEST_COLUMN_INTENT_ACTION,
+ };
private HttpClient mHttpClient;
@@ -84,10 +90,6 @@ public class SuggestionProvider extends ContentProvider {
return true;
}
- private static ArrayListCursor makeEmptyCursor() {
- return new ArrayListCursor(COLUMNS, new ArrayList<ArrayList>());
- }
-
/**
* This will always return {@link SearchManager#SUGGEST_MIME_TYPE} as this
* provider is purely to provide suggestions.
@@ -106,25 +108,43 @@ public class SuggestionProvider extends ContentProvider {
String[] selectionArgs, String sortOrder) {
String query = selectionArgs[0];
if (TextUtils.isEmpty(query)) {
-
- /* Can't pass back null, things blow up */
- return makeEmptyCursor();
+ 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*
+ // 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();
- mSuggestUri = getContext().getResources().getString(R.string.google_search_base,
- l.getLanguage(),
- l.getCountry().toLowerCase())
+ 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=";
}
@@ -133,7 +153,7 @@ public class SuggestionProvider extends ContentProvider {
method.setEntity(content);
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
@@ -151,14 +171,28 @@ public class SuggestionProvider extends ContentProvider {
} catch (JSONException e) {
Log.w(LOG_TAG, "Error", e);
}
- return makeEmptyCursor();
+ return null;
+ }
+
+ 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.
*/
@@ -193,6 +227,8 @@ public class SuggestionProvider extends ContentProvider {
} catch (JSONException e) {
Log.w(LOG_TAG, "Error", e);
}
+ } else if (column == 4) {
+ return Intent.ACTION_WEB_SEARCH;
}
}
return null;
@@ -231,7 +267,7 @@ public class SuggestionProvider extends ContentProvider {
throw new UnsupportedOperationException();
}
}
-
+
@Override
public Uri insert(Uri uri, ContentValues values) {
throw new UnsupportedOperationException();