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 18 package com.android.browser; 19 20 import android.app.Activity; 21 import android.app.SearchManager; 22 import android.content.ContentResolver; 23 import android.content.Context; 24 import android.content.Intent; 25 import android.net.Uri; 26 import android.nfc.NfcAdapter; 27 import android.os.AsyncTask; 28 import android.os.Bundle; 29 import android.provider.Browser; 30 import android.provider.MediaStore; 31 import android.text.TextUtils; 32 import android.util.Patterns; 33 34 import com.android.browser.UI.ComboViews; 35 import com.android.browser.search.SearchEngine; 36 import com.android.common.Search; 37 38 import java.util.HashMap; 39 import java.util.Iterator; 40 import java.util.Map; 41 42 /** 43 * Handle all browser related intents 44 */ 45 public class IntentHandler { 46 47 // "source" parameter for Google search suggested by the browser 48 final static String GOOGLE_SEARCH_SOURCE_SUGGEST = "browser-suggest"; 49 // "source" parameter for Google search from unknown source 50 final static String GOOGLE_SEARCH_SOURCE_UNKNOWN = "unknown"; 51 52 /* package */ static final UrlData EMPTY_URL_DATA = new UrlData(null); 53 54 private Activity mActivity; 55 private Controller mController; 56 private TabControl mTabControl; 57 private BrowserSettings mSettings; 58 IntentHandler(Activity browser, Controller controller)59 public IntentHandler(Activity browser, Controller controller) { 60 mActivity = browser; 61 mController = controller; 62 mTabControl = mController.getTabControl(); 63 mSettings = controller.getSettings(); 64 } 65 onNewIntent(Intent intent)66 void onNewIntent(Intent intent) { 67 Tab current = mTabControl.getCurrentTab(); 68 // When a tab is closed on exit, the current tab index is set to -1. 69 // Reset before proceed as Browser requires the current tab to be set. 70 if (current == null) { 71 // Try to reset the tab in case the index was incorrect. 72 current = mTabControl.getTab(0); 73 if (current == null) { 74 // No tabs at all so just ignore this intent. 75 return; 76 } 77 mController.setActiveTab(current); 78 } 79 final String action = intent.getAction(); 80 final int flags = intent.getFlags(); 81 if (Intent.ACTION_MAIN.equals(action) || 82 (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 83 // just resume the browser 84 return; 85 } 86 if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(action)) { 87 mController.bookmarksOrHistoryPicker(ComboViews.Bookmarks); 88 return; 89 } 90 91 // In case the SearchDialog is open. 92 ((SearchManager) mActivity.getSystemService(Context.SEARCH_SERVICE)) 93 .stopSearch(); 94 if (Intent.ACTION_VIEW.equals(action) 95 || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action) 96 || Intent.ACTION_SEARCH.equals(action) 97 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) 98 || Intent.ACTION_WEB_SEARCH.equals(action)) { 99 // If this was a search request (e.g. search query directly typed into the address bar), 100 // pass it on to the default web search provider. 101 if (handleWebSearchIntent(mActivity, mController, intent)) { 102 return; 103 } 104 105 UrlData urlData = getUrlDataFromIntent(intent); 106 if (urlData.isEmpty()) { 107 urlData = new UrlData(mSettings.getHomePage()); 108 } 109 110 // If url is to view private data files, don't allow. 111 Uri uri = intent.getData(); 112 if (uri != null && uri.getScheme().toLowerCase().startsWith("file") && 113 uri.getPath().startsWith(mActivity.getDatabasePath("foo").getParent())) { 114 return; 115 } 116 117 if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false) 118 || urlData.isPreloaded()) { 119 Tab t = mController.openTab(urlData); 120 return; 121 } 122 /* 123 * TODO: Don't allow javascript URIs 124 * 0) If this is a javascript: URI, *always* open a new tab 125 * 1) If the URL is already opened, switch to that tab 126 * 2-phone) Reuse tab with same appId 127 * 2-tablet) Open new tab 128 */ 129 final String appId = intent 130 .getStringExtra(Browser.EXTRA_APPLICATION_ID); 131 if (!TextUtils.isEmpty(urlData.mUrl) && 132 urlData.mUrl.startsWith("javascript:")) { 133 // Always open javascript: URIs in new tabs 134 mController.openTab(urlData); 135 return; 136 } 137 if (Intent.ACTION_VIEW.equals(action) 138 && (appId != null) 139 && appId.startsWith(mActivity.getPackageName())) { 140 Tab appTab = mTabControl.getTabFromAppId(appId); 141 if ((appTab != null) && (appTab == mController.getCurrentTab())) { 142 mController.switchToTab(appTab); 143 mController.loadUrlDataIn(appTab, urlData); 144 return; 145 } 146 } 147 if (Intent.ACTION_VIEW.equals(action) 148 && !mActivity.getPackageName().equals(appId)) { 149 if (!BrowserActivity.isTablet(mActivity) 150 && !mSettings.allowAppTabs()) { 151 Tab appTab = mTabControl.getTabFromAppId(appId); 152 if (appTab != null) { 153 mController.reuseTab(appTab, urlData); 154 return; 155 } 156 } 157 // No matching application tab, try to find a regular tab 158 // with a matching url. 159 Tab appTab = mTabControl.findTabWithUrl(urlData.mUrl); 160 if (appTab != null) { 161 // Transfer ownership 162 appTab.setAppId(appId); 163 if (current != appTab) { 164 mController.switchToTab(appTab); 165 } 166 // Otherwise, we are already viewing the correct tab. 167 } else { 168 // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url 169 // will be opened in a new tab unless we have reached 170 // MAX_TABS. Then the url will be opened in the current 171 // tab. If a new tab is created, it will have "true" for 172 // exit on close. 173 Tab tab = mController.openTab(urlData); 174 if (tab != null) { 175 tab.setAppId(appId); 176 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) { 177 tab.setCloseOnBack(true); 178 } 179 } 180 } 181 } else { 182 // Get rid of the subwindow if it exists 183 mController.dismissSubWindow(current); 184 // If the current Tab is being used as an application tab, 185 // remove the association, since the new Intent means that it is 186 // no longer associated with that application. 187 current.setAppId(null); 188 mController.loadUrlDataIn(current, urlData); 189 } 190 } 191 } 192 getUrlDataFromIntent(Intent intent)193 protected static UrlData getUrlDataFromIntent(Intent intent) { 194 String url = ""; 195 Map<String, String> headers = null; 196 PreloadedTabControl preloaded = null; 197 String preloadedSearchBoxQuery = null; 198 if (intent != null 199 && (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) { 200 final String action = intent.getAction(); 201 if (Intent.ACTION_VIEW.equals(action) || 202 NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) { 203 url = UrlUtils.smartUrlFilter(intent.getData()); 204 if (url != null && url.startsWith("http")) { 205 final Bundle pairs = intent 206 .getBundleExtra(Browser.EXTRA_HEADERS); 207 if (pairs != null && !pairs.isEmpty()) { 208 Iterator<String> iter = pairs.keySet().iterator(); 209 headers = new HashMap<String, String>(); 210 while (iter.hasNext()) { 211 String key = iter.next(); 212 headers.put(key, pairs.getString(key)); 213 } 214 } 215 } 216 if (intent.hasExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID)) { 217 String id = intent.getStringExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID); 218 preloadedSearchBoxQuery = intent.getStringExtra( 219 PreloadRequestReceiver.EXTRA_SEARCHBOX_SETQUERY); 220 preloaded = Preloader.getInstance().getPreloadedTab(id); 221 } 222 } else if (Intent.ACTION_SEARCH.equals(action) 223 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) 224 || Intent.ACTION_WEB_SEARCH.equals(action)) { 225 url = intent.getStringExtra(SearchManager.QUERY); 226 if (url != null) { 227 // In general, we shouldn't modify URL from Intent. 228 // But currently, we get the user-typed URL from search box as well. 229 url = UrlUtils.fixUrl(url); 230 url = UrlUtils.smartUrlFilter(url); 231 String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&"; 232 if (url.contains(searchSource)) { 233 String source = null; 234 final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA); 235 if (appData != null) { 236 source = appData.getString(Search.SOURCE); 237 } 238 if (TextUtils.isEmpty(source)) { 239 source = GOOGLE_SEARCH_SOURCE_UNKNOWN; 240 } 241 url = url.replace(searchSource, "&source=android-"+source+"&"); 242 } 243 } 244 } 245 } 246 return new UrlData(url, headers, intent, preloaded, preloadedSearchBoxQuery); 247 } 248 249 /** 250 * Launches the default web search activity with the query parameters if the given intent's data 251 * are identified as plain search terms and not URLs/shortcuts. 252 * @return true if the intent was handled and web search activity was launched, false if not. 253 */ handleWebSearchIntent(Activity activity, Controller controller, Intent intent)254 static boolean handleWebSearchIntent(Activity activity, 255 Controller controller, Intent intent) { 256 if (intent == null) return false; 257 258 String url = null; 259 final String action = intent.getAction(); 260 if (Intent.ACTION_VIEW.equals(action)) { 261 Uri data = intent.getData(); 262 if (data != null) url = data.toString(); 263 } else if (Intent.ACTION_SEARCH.equals(action) 264 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) 265 || Intent.ACTION_WEB_SEARCH.equals(action)) { 266 url = intent.getStringExtra(SearchManager.QUERY); 267 } 268 return handleWebSearchRequest(activity, controller, url, 269 intent.getBundleExtra(SearchManager.APP_DATA), 270 intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); 271 } 272 273 /** 274 * Launches the default web search activity with the query parameters if the given url string 275 * was identified as plain search terms and not URL/shortcut. 276 * @return true if the request was handled and web search activity was launched, false if not. 277 */ handleWebSearchRequest(Activity activity, Controller controller, String inUrl, Bundle appData, String extraData)278 private static boolean handleWebSearchRequest(Activity activity, 279 Controller controller, String inUrl, Bundle appData, 280 String extraData) { 281 if (inUrl == null) return false; 282 283 // In general, we shouldn't modify URL from Intent. 284 // But currently, we get the user-typed URL from search box as well. 285 String url = UrlUtils.fixUrl(inUrl).trim(); 286 if (TextUtils.isEmpty(url)) return false; 287 288 // URLs are handled by the regular flow of control, so 289 // return early. 290 if (Patterns.WEB_URL.matcher(url).matches() 291 || UrlUtils.ACCEPTED_URI_SCHEMA.matcher(url).matches()) { 292 return false; 293 } 294 295 final ContentResolver cr = activity.getContentResolver(); 296 final String newUrl = url; 297 if (controller == null || controller.getTabControl() == null 298 || controller.getTabControl().getCurrentWebView() == null 299 || !controller.getTabControl().getCurrentWebView() 300 .isPrivateBrowsingEnabled()) { 301 new AsyncTask<Void, Void, Void>() { 302 @Override 303 protected Void doInBackground(Void... unused) { 304 Browser.addSearchUrl(cr, newUrl); 305 return null; 306 } 307 }.execute(); 308 } 309 310 SearchEngine searchEngine = BrowserSettings.getInstance().getSearchEngine(); 311 if (searchEngine == null) return false; 312 searchEngine.startSearch(activity, url, appData, extraData); 313 314 return true; 315 } 316 317 /** 318 * A UrlData class to abstract how the content will be set to WebView. 319 * This base class uses loadUrl to show the content. 320 */ 321 static class UrlData { 322 final String mUrl; 323 final Map<String, String> mHeaders; 324 final PreloadedTabControl mPreloadedTab; 325 final String mSearchBoxQueryToSubmit; 326 final boolean mDisableUrlOverride; 327 UrlData(String url)328 UrlData(String url) { 329 this.mUrl = url; 330 this.mHeaders = null; 331 this.mPreloadedTab = null; 332 this.mSearchBoxQueryToSubmit = null; 333 this.mDisableUrlOverride = false; 334 } 335 UrlData(String url, Map<String, String> headers, Intent intent)336 UrlData(String url, Map<String, String> headers, Intent intent) { 337 this(url, headers, intent, null, null); 338 } 339 UrlData(String url, Map<String, String> headers, Intent intent, PreloadedTabControl preloaded, String searchBoxQueryToSubmit)340 UrlData(String url, Map<String, String> headers, Intent intent, 341 PreloadedTabControl preloaded, String searchBoxQueryToSubmit) { 342 this.mUrl = url; 343 this.mHeaders = headers; 344 this.mPreloadedTab = preloaded; 345 this.mSearchBoxQueryToSubmit = searchBoxQueryToSubmit; 346 if (intent != null) { 347 mDisableUrlOverride = intent.getBooleanExtra( 348 BrowserActivity.EXTRA_DISABLE_URL_OVERRIDE, false); 349 } else { 350 mDisableUrlOverride = false; 351 } 352 } 353 isEmpty()354 boolean isEmpty() { 355 return (mUrl == null || mUrl.length() == 0); 356 } 357 isPreloaded()358 boolean isPreloaded() { 359 return mPreloadedTab != null; 360 } 361 getPreloadedTab()362 PreloadedTabControl getPreloadedTab() { 363 return mPreloadedTab; 364 } 365 getSearchBoxQueryToSubmit()366 String getSearchBoxQueryToSubmit() { 367 return mSearchBoxQueryToSubmit; 368 } 369 } 370 371 } 372