1e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert/* 2e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * Copyright (C) 2008 The Android Open Source Project 3e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * 4e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License"); 5e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * you may not use this file except in compliance with the License. 6e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * You may obtain a copy of the License at 7e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * 8e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * http://www.apache.org/licenses/LICENSE-2.0 9e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * 10e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * Unless required by applicable law or agreed to in writing, software 11e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS, 12e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * See the License for the specific language governing permissions and 14e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * limitations under the License. 15e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert */ 16e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 1718c681a8ab3c5227fb536501572c96b92c914a07Bjorn Bringertpackage com.android.quicksearchbox.google; 18e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 193f71a11f8858b9164ca83e8d2f558dd8a8a5e2deBjorn Bringertimport com.android.common.Search; 20c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamathimport com.android.quicksearchbox.QsbApplication; 21e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 22e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport android.app.Activity; 2391da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringertimport android.app.PendingIntent; 24e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport android.app.SearchManager; 2591da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringertimport android.content.ActivityNotFoundException; 2691da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringertimport android.content.Context; 27e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport android.content.Intent; 2891da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringertimport android.location.Location; 29e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport android.net.Uri; 30e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport android.os.Bundle; 31e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport android.provider.Browser; 32e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport android.text.TextUtils; 33e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport android.util.Log; 34e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 35e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport java.io.UnsupportedEncodingException; 36e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport java.net.URLEncoder; 37e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertimport java.util.Locale; 38e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 39e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert/** 40e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * This class is purely here to get search queries and route them to 41e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert * the global {@link Intent#ACTION_WEB_SEARCH}. 42e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert */ 43e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringertpublic class GoogleSearch extends Activity { 44e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert private static final String TAG = "GoogleSearch"; 45c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood private static final boolean DBG = false; 46e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 47c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath // Used to figure out which domain to base search requests 48c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath // on. 49c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath private SearchBaseUrlHelper mSearchDomainHelper; 50e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 51e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert // "source" parameter for Google search requests from unknown sources (e.g. apps). This will get 52e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert // prefixed with the string 'android-' before being sent on the wire. 53e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert final static String GOOGLE_SEARCH_SOURCE_UNKNOWN = "unknown"; 54e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 55e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert @Override 56e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert protected void onCreate(Bundle savedInstanceState) { 57e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert super.onCreate(savedInstanceState); 58e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert Intent intent = getIntent(); 59e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert String action = intent != null ? intent.getAction() : null; 60c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath 61c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath // This should probably be moved so as to 62c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath // send out the request to /checksearchdomain as early as possible. 63c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath mSearchDomainHelper = QsbApplication.get(this).getSearchBaseUrlHelper(); 64c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath 65e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert if (Intent.ACTION_WEB_SEARCH.equals(action) || Intent.ACTION_SEARCH.equals(action)) { 66e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert handleWebSearchIntent(intent); 67e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert } 68c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath 69e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert finish(); 70e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert } 71e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 72e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert /** 73c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood * Construct the language code (hl= paramater) for the given locale. 74e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert */ 75c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood public static String getLanguage(Locale locale) { 76c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood String language = locale.getLanguage(); 77c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood StringBuilder hl = new StringBuilder(language); 78c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood String country = locale.getCountry(); 79bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert 80bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert if (!TextUtils.isEmpty(country) && useLangCountryHl(language, country)) { 81c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood hl.append('-'); 82c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood hl.append(country); 83c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood } 84c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath 85c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood if (DBG) Log.d(TAG, "language " + language + ", country " + country + " -> hl=" + hl); 86c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood return hl.toString(); 87c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood } 88c4e96e6c09422ab54984144ad618b2ecefb4b891Mathew Inwood 89bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert // TODO: This is a workaround for bug 3232296. When that is fixed, this method can be removed. 90bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert private static boolean useLangCountryHl(String language, String country) { 91bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert // lang-country is currently only supported for a small number of locales 92bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert if ("en".equals(language)) { 93bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert return "GB".equals(country); 94bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert } else if ("zh".equals(language)) { 95bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert return "CN".equals(country) || "TW".equals(country); 96bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert } else if ("pt".equals(language)) { 97bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert return "BR".equals(country) || "PT".equals(country); 98bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert } else { 99bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert return false; 100bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert } 101bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert } 102bfafb70b43802f4ca37aab3ea7f6e9568aa7361fBjorn Bringert 103e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert private void handleWebSearchIntent(Intent intent) { 10491da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert Intent launchUriIntent = createLaunchUriIntentFromSearchIntent(intent); 10591da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert PendingIntent pending = 10691da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert intent.getParcelableExtra(SearchManager.EXTRA_WEB_SEARCH_PENDINGINTENT); 10791da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert if (pending == null || !launchPendingIntent(pending, launchUriIntent)) { 10891da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert launchIntent(launchUriIntent); 10991da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert } 11091da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert } 11191da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert 11291da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert private Intent createLaunchUriIntentFromSearchIntent(Intent intent) { 113e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert String query = intent.getStringExtra(SearchManager.QUERY); 114e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert if (TextUtils.isEmpty(query)) { 115e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert Log.w(TAG, "Got search intent with no query."); 11691da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert return null; 117e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert } 118e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 119e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert // If the caller specified a 'source' url parameter, use that and if not use default. 120e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA); 121e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert String source = GOOGLE_SEARCH_SOURCE_UNKNOWN; 122e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert if (appSearchData != null) { 1233f71a11f8858b9164ca83e8d2f558dd8a8a5e2deBjorn Bringert source = appSearchData.getString(Search.SOURCE); 124e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert } 125e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 126e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert // The browser can pass along an application id which it uses to figure out which 127e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert // window to place a new search into. So if this exists, we'll pass it back to 128e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert // the browser. Otherwise, add our own package name as the application id, so that 129e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert // the browser can organize all searches launched from this provider together. 130e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert String applicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID); 131e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert if (applicationId == null) { 132e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert applicationId = getPackageName(); 133e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert } 134e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert 135e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert try { 136c953ef06f0fc1fb4157fe67aa145cf702ee204d0Narayan Kamath String searchUri = mSearchDomainHelper.getSearchBaseUrl() 137e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert + "&source=android-" + source 138e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert + "&q=" + URLEncoder.encode(query, "UTF-8"); 139e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert Intent launchUriIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)); 140e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert launchUriIntent.putExtra(Browser.EXTRA_APPLICATION_ID, applicationId); 141e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert launchUriIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 14291da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert return launchUriIntent; 143e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert } catch (UnsupportedEncodingException e) { 144e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert Log.w(TAG, "Error", e); 14591da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert return null; 14691da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert } 14791da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert 14891da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert } 14991da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert 15091da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert private void launchIntent(Intent intent) { 15191da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert try { 15291da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert Log.i(TAG, "Launching intent: " + intent.toUri(0)); 15391da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert startActivity(intent); 15491da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert } catch (ActivityNotFoundException ex) { 15591da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert Log.w(TAG, "No activity found to handle: " + intent); 15691da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert } 15791da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert } 15891da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert 15991da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert private boolean launchPendingIntent(PendingIntent pending, Intent fillIn) { 16091da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert try { 16191da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert pending.send(this, Activity.RESULT_OK, fillIn); 16291da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert return true; 16391da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert } catch (PendingIntent.CanceledException ex) { 16491da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert Log.i(TAG, "Pending intent cancelled: " + pending); 16591da7b135300cbbe482e6d1da64bd52ce629245dBjorn Bringert return false; 166e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert } 167e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert } 168e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert 169e72a6418e84a89d2bfd4e9d22282eca95357e0daBjorn Bringert} 170