1 /* 2 * Copyright (C) 2008 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.common.Search; 20 import com.android.quicksearchbox.QsbApplication; 21 22 import android.app.Activity; 23 import android.app.PendingIntent; 24 import android.app.SearchManager; 25 import android.content.ActivityNotFoundException; 26 import android.content.Context; 27 import android.content.Intent; 28 import android.location.Location; 29 import android.net.Uri; 30 import android.os.Bundle; 31 import android.provider.Browser; 32 import android.text.TextUtils; 33 import android.util.Log; 34 35 import java.io.UnsupportedEncodingException; 36 import java.net.URLEncoder; 37 import java.util.Locale; 38 39 /** 40 * This class is purely here to get search queries and route them to 41 * the global {@link Intent#ACTION_WEB_SEARCH}. 42 */ 43 public class GoogleSearch extends Activity { 44 private static final String TAG = "GoogleSearch"; 45 private static final boolean DBG = false; 46 47 // Used to figure out which domain to base search requests 48 // on. 49 private SearchBaseUrlHelper mSearchDomainHelper; 50 51 // "source" parameter for Google search requests from unknown sources (e.g. apps). This will get 52 // prefixed with the string 'android-' before being sent on the wire. 53 final static String GOOGLE_SEARCH_SOURCE_UNKNOWN = "unknown"; 54 55 @Override onCreate(Bundle savedInstanceState)56 protected void onCreate(Bundle savedInstanceState) { 57 super.onCreate(savedInstanceState); 58 Intent intent = getIntent(); 59 String action = intent != null ? intent.getAction() : null; 60 61 // This should probably be moved so as to 62 // send out the request to /checksearchdomain as early as possible. 63 mSearchDomainHelper = QsbApplication.get(this).getSearchBaseUrlHelper(); 64 65 if (Intent.ACTION_WEB_SEARCH.equals(action) || Intent.ACTION_SEARCH.equals(action)) { 66 handleWebSearchIntent(intent); 67 } 68 69 finish(); 70 } 71 72 /** 73 * Construct the language code (hl= paramater) for the given locale. 74 */ getLanguage(Locale locale)75 public static String getLanguage(Locale locale) { 76 String language = locale.getLanguage(); 77 StringBuilder hl = new StringBuilder(language); 78 String country = locale.getCountry(); 79 80 if (!TextUtils.isEmpty(country) && useLangCountryHl(language, country)) { 81 hl.append('-'); 82 hl.append(country); 83 } 84 85 if (DBG) Log.d(TAG, "language " + language + ", country " + country + " -> hl=" + hl); 86 return hl.toString(); 87 } 88 89 // TODO: This is a workaround for bug 3232296. When that is fixed, this method can be removed. useLangCountryHl(String language, String country)90 private static boolean useLangCountryHl(String language, String country) { 91 // lang-country is currently only supported for a small number of locales 92 if ("en".equals(language)) { 93 return "GB".equals(country); 94 } else if ("zh".equals(language)) { 95 return "CN".equals(country) || "TW".equals(country); 96 } else if ("pt".equals(language)) { 97 return "BR".equals(country) || "PT".equals(country); 98 } else { 99 return false; 100 } 101 } 102 handleWebSearchIntent(Intent intent)103 private void handleWebSearchIntent(Intent intent) { 104 Intent launchUriIntent = createLaunchUriIntentFromSearchIntent(intent); 105 PendingIntent pending = 106 intent.getParcelableExtra(SearchManager.EXTRA_WEB_SEARCH_PENDINGINTENT); 107 if (pending == null || !launchPendingIntent(pending, launchUriIntent)) { 108 launchIntent(launchUriIntent); 109 } 110 } 111 createLaunchUriIntentFromSearchIntent(Intent intent)112 private Intent createLaunchUriIntentFromSearchIntent(Intent intent) { 113 String query = intent.getStringExtra(SearchManager.QUERY); 114 if (TextUtils.isEmpty(query)) { 115 Log.w(TAG, "Got search intent with no query."); 116 return null; 117 } 118 119 // If the caller specified a 'source' url parameter, use that and if not use default. 120 Bundle appSearchData = intent.getBundleExtra(SearchManager.APP_DATA); 121 String source = GOOGLE_SEARCH_SOURCE_UNKNOWN; 122 if (appSearchData != null) { 123 source = appSearchData.getString(Search.SOURCE); 124 } 125 126 // The browser can pass along an application id which it uses to figure out which 127 // window to place a new search into. So if this exists, we'll pass it back to 128 // the browser. Otherwise, add our own package name as the application id, so that 129 // the browser can organize all searches launched from this provider together. 130 String applicationId = intent.getStringExtra(Browser.EXTRA_APPLICATION_ID); 131 if (applicationId == null) { 132 applicationId = getPackageName(); 133 } 134 135 try { 136 String searchUri = mSearchDomainHelper.getSearchBaseUrl() 137 + "&source=android-" + source 138 + "&q=" + URLEncoder.encode(query, "UTF-8"); 139 Intent launchUriIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(searchUri)); 140 launchUriIntent.putExtra(Browser.EXTRA_APPLICATION_ID, applicationId); 141 launchUriIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 142 return launchUriIntent; 143 } catch (UnsupportedEncodingException e) { 144 Log.w(TAG, "Error", e); 145 return null; 146 } 147 148 } 149 launchIntent(Intent intent)150 private void launchIntent(Intent intent) { 151 try { 152 Log.i(TAG, "Launching intent: " + intent.toUri(0)); 153 startActivity(intent); 154 } catch (ActivityNotFoundException ex) { 155 Log.w(TAG, "No activity found to handle: " + intent); 156 } 157 } 158 launchPendingIntent(PendingIntent pending, Intent fillIn)159 private boolean launchPendingIntent(PendingIntent pending, Intent fillIn) { 160 try { 161 pending.send(this, Activity.RESULT_OK, fillIn); 162 return true; 163 } catch (PendingIntent.CanceledException ex) { 164 Log.i(TAG, "Pending intent cancelled: " + pending); 165 return false; 166 } 167 } 168 169 } 170