blob: 9e2bddfd2af92701dd2848bbbfea4a8a13328f25 [file] [log] [blame]
Michael Kolb8233fac2010-10-26 16:08:53 -07001/*
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
Bijan Amirzada41242f22014-03-21 12:12:18 -070018package com.android.browser;
Michael Kolb8233fac2010-10-26 16:08:53 -070019
Michael Kolb8233fac2010-10-26 16:08:53 -070020import android.app.Activity;
21import android.app.SearchManager;
22import android.content.ContentResolver;
23import android.content.Context;
24import android.content.Intent;
25import android.net.Uri;
Jeff Hamiltonfd77aaa2011-06-17 16:19:33 -050026import android.nfc.NfcAdapter;
Michael Kolb8233fac2010-10-26 16:08:53 -070027import android.os.AsyncTask;
28import android.os.Bundle;
Michael Kolb8233fac2010-10-26 16:08:53 -070029import android.provider.MediaStore;
Michael Kolb8233fac2010-10-26 16:08:53 -070030import android.text.TextUtils;
31import android.util.Patterns;
32
Bijan Amirzada41242f22014-03-21 12:12:18 -070033import com.android.browser.UI.ComboViews;
Bijan Amirzada3f04dc72014-06-25 11:48:36 -070034import com.android.browser.platformsupport.Browser;
Bijan Amirzada41242f22014-03-21 12:12:18 -070035import com.android.browser.search.SearchEngine;
Michael Kolbe28b3472011-08-04 16:54:31 -070036
Michael Kolb8233fac2010-10-26 16:08:53 -070037import java.util.HashMap;
38import java.util.Iterator;
39import java.util.Map;
40
41/**
42 * Handle all browser related intents
43 */
44public class IntentHandler {
45
46 // "source" parameter for Google search suggested by the browser
47 final static String GOOGLE_SEARCH_SOURCE_SUGGEST = "browser-suggest";
48 // "source" parameter for Google search from unknown source
49 final static String GOOGLE_SEARCH_SOURCE_UNKNOWN = "unknown";
50
51 /* package */ static final UrlData EMPTY_URL_DATA = new UrlData(null);
52
53 private Activity mActivity;
54 private Controller mController;
55 private TabControl mTabControl;
56 private BrowserSettings mSettings;
57
58 public IntentHandler(Activity browser, Controller controller) {
59 mActivity = browser;
60 mController = controller;
61 mTabControl = mController.getTabControl();
62 mSettings = controller.getSettings();
63 }
64
65 void onNewIntent(Intent intent) {
Tarun Nainani87a86682015-02-05 11:47:35 -080066 int requestCode = intent.getIntExtra(Controller.EXTRA_REQUEST_CODE, -1);
67 if (requestCode >= Controller.COMBO_VIEW && requestCode <= Controller.MY_NAVIGATION) {
68 int resultCode = intent.getIntExtra(Controller.EXTRA_RESULT_CODE, Activity.RESULT_OK);
69 mController.onActivityResult(requestCode, resultCode, intent);
70 return;
71 }
72
Michael Kolb8233fac2010-10-26 16:08:53 -070073 Tab current = mTabControl.getCurrentTab();
74 // When a tab is closed on exit, the current tab index is set to -1.
75 // Reset before proceed as Browser requires the current tab to be set.
76 if (current == null) {
77 // Try to reset the tab in case the index was incorrect.
78 current = mTabControl.getTab(0);
79 if (current == null) {
80 // No tabs at all so just ignore this intent.
81 return;
82 }
83 mController.setActiveTab(current);
Michael Kolb8233fac2010-10-26 16:08:53 -070084 }
85 final String action = intent.getAction();
86 final int flags = intent.getFlags();
87 if (Intent.ACTION_MAIN.equals(action) ||
88 (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
89 // just resume the browser
90 return;
91 }
John Reck439c9a52010-12-14 10:04:39 -080092 if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(action)) {
Michael Kolb315d5022011-10-13 12:47:11 -070093 mController.bookmarksOrHistoryPicker(ComboViews.Bookmarks);
John Reck439c9a52010-12-14 10:04:39 -080094 return;
95 }
John Reck07af2b82011-01-18 14:24:43 -080096
Michael Kolb8233fac2010-10-26 16:08:53 -070097 // In case the SearchDialog is open.
98 ((SearchManager) mActivity.getSystemService(Context.SEARCH_SERVICE))
99 .stopSearch();
Michael Kolb8233fac2010-10-26 16:08:53 -0700100 if (Intent.ACTION_VIEW.equals(action)
Jeff Hamiltonfd77aaa2011-06-17 16:19:33 -0500101 || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)
Michael Kolb8233fac2010-10-26 16:08:53 -0700102 || Intent.ACTION_SEARCH.equals(action)
103 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700104 || Intent.ACTION_WEB_SEARCH.equals(action)) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700105 // If this was a search request (e.g. search query directly typed into the address bar),
106 // pass it on to the default web search provider.
107 if (handleWebSearchIntent(mActivity, mController, intent)) {
108 return;
109 }
110
111 UrlData urlData = getUrlDataFromIntent(intent);
112 if (urlData.isEmpty()) {
113 urlData = new UrlData(mSettings.getHomePage());
114 }
115
Michael Kolb14612442011-06-24 13:06:29 -0700116 if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)
117 || urlData.isPreloaded()) {
118 Tab t = mController.openTab(urlData);
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700119 t.setDerivedFromIntent(true);
Leon Scroggins2ed6e312011-02-22 16:37:23 -0500120 return;
121 }
John Reckdb22ec42011-06-29 11:31:24 -0700122 /*
123 * TODO: Don't allow javascript URIs
124 * 0) If this is a javascript: URI, *always* open a new tab
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700125 * 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
John Reckdb22ec42011-06-29 11:31:24 -0700128 */
Michael Kolb8233fac2010-10-26 16:08:53 -0700129 final String appId = intent
130 .getStringExtra(Browser.EXTRA_APPLICATION_ID);
John Reck26b18322011-06-21 13:08:58 -0700131 if (!TextUtils.isEmpty(urlData.mUrl) &&
132 urlData.mUrl.startsWith("javascript:")) {
133 // Always open javascript: URIs in new tabs
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700134 Tab jsTab = mController.openTab(urlData);
135 jsTab.setDerivedFromIntent(true);
John Reck26b18322011-06-21 13:08:58 -0700136 return;
137 }
Michael Kolbf1286a42012-04-17 14:10:52 -0700138 if (Intent.ACTION_VIEW.equals(action)
139 && (appId != null)
140 && appId.startsWith(mActivity.getPackageName())) {
141 Tab appTab = mTabControl.getTabFromAppId(appId);
142 if ((appTab != null) && (appTab == mController.getCurrentTab())) {
143 mController.switchToTab(appTab);
144 mController.loadUrlDataIn(appTab, urlData);
145 return;
146 }
147 }
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700148 if (Intent.ACTION_VIEW.equals(action)
149 && !mActivity.getPackageName().equals(appId)) {
Michael Kolb8d772b02012-01-19 13:56:00 -0800150 if (!BrowserActivity.isTablet(mActivity)
151 && !mSettings.allowAppTabs()) {
Michael Kolbc831b632011-05-11 09:30:34 -0700152 Tab appTab = mTabControl.getTabFromAppId(appId);
Michael Kolbaf0d3342011-03-11 11:30:16 -0800153 if (appTab != null) {
John Reck26b18322011-06-21 13:08:58 -0700154 mController.reuseTab(appTab, urlData);
Michael Kolbaf0d3342011-03-11 11:30:16 -0800155 return;
Michael Kolbaf0d3342011-03-11 11:30:16 -0800156 }
John Reckdb22ec42011-06-29 11:31:24 -0700157 }
158 // No matching application tab, try to find a regular tab
159 // with a matching url.
160 Tab appTab = mTabControl.findTabWithUrl(urlData.mUrl);
161 if (appTab != null) {
162 // Transfer ownership
163 appTab.setAppId(appId);
164 if (current != appTab) {
165 mController.switchToTab(appTab);
166 }
167 // Otherwise, we are already viewing the correct tab.
Michael Kolb8233fac2010-10-26 16:08:53 -0700168 } else {
John Reckdb22ec42011-06-29 11:31:24 -0700169 // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url
170 // will be opened in a new tab unless we have reached
171 // MAX_TABS. Then the url will be opened in the current
172 // tab. If a new tab is created, it will have "true" for
173 // exit on close.
174 Tab tab = mController.openTab(urlData);
175 if (tab != null) {
176 tab.setAppId(appId);
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700177 tab.setDerivedFromIntent(true);
Michael Kolbe28b3472011-08-04 16:54:31 -0700178 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
179 tab.setCloseOnBack(true);
180 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700181 }
182 }
183 } else {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800184 if (!urlData.isEmpty()
Michael Kolb8233fac2010-10-26 16:08:53 -0700185 && urlData.mUrl.startsWith("about:debug")) {
186 if ("about:debug.dom".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800187 current.getWebView().dumpDomTree(false);
Michael Kolb8233fac2010-10-26 16:08:53 -0700188 } else if ("about:debug.dom.file".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800189 current.getWebView().dumpDomTree(true);
Michael Kolb8233fac2010-10-26 16:08:53 -0700190 } else if ("about:debug.render".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800191 current.getWebView().dumpRenderTree(false);
Michael Kolb8233fac2010-10-26 16:08:53 -0700192 } else if ("about:debug.render.file".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800193 current.getWebView().dumpRenderTree(true);
Michael Kolb8233fac2010-10-26 16:08:53 -0700194 } else if ("about:debug.display".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800195 current.getWebView().dumpDisplayTree();
Cary Clark4f4cb6b2011-01-18 13:03:42 -0500196 } else if ("about:debug.nav".equals(urlData.mUrl)) {
197 current.getWebView().debugDump();
Michael Kolb8233fac2010-10-26 16:08:53 -0700198 } else {
John Reck35e9dd62011-04-25 09:01:54 -0700199 mSettings.toggleDebugSettings();
Michael Kolb8233fac2010-10-26 16:08:53 -0700200 }
201 return;
202 }
203 // Get rid of the subwindow if it exists
204 mController.dismissSubWindow(current);
205 // If the current Tab is being used as an application tab,
206 // remove the association, since the new Intent means that it is
207 // no longer associated with that application.
208 current.setAppId(null);
209 mController.loadUrlDataIn(current, urlData);
210 }
211 }
212 }
213
Michael Kolb14612442011-06-24 13:06:29 -0700214 protected static UrlData getUrlDataFromIntent(Intent intent) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700215 String url = "";
216 Map<String, String> headers = null;
Mathew Inwood29721c22011-06-29 17:55:24 +0100217 PreloadedTabControl preloaded = null;
218 String preloadedSearchBoxQuery = null;
Leon Scrogginse1cab102011-01-04 10:50:13 -0500219 if (intent != null
220 && (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700221 final String action = intent.getAction();
Jeff Hamiltonfd77aaa2011-06-17 16:19:33 -0500222 if (Intent.ACTION_VIEW.equals(action) ||
223 NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700224 url = UrlUtils.smartUrlFilter(intent.getData());
Michael Kolb8233fac2010-10-26 16:08:53 -0700225 if (url != null && url.startsWith("http")) {
226 final Bundle pairs = intent
227 .getBundleExtra(Browser.EXTRA_HEADERS);
228 if (pairs != null && !pairs.isEmpty()) {
229 Iterator<String> iter = pairs.keySet().iterator();
230 headers = new HashMap<String, String>();
231 while (iter.hasNext()) {
232 String key = iter.next();
233 headers.put(key, pairs.getString(key));
234 }
235 }
236 }
Michael Kolb14612442011-06-24 13:06:29 -0700237 if (intent.hasExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID)) {
238 String id = intent.getStringExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID);
Mathew Inwood29721c22011-06-29 17:55:24 +0100239 preloadedSearchBoxQuery = intent.getStringExtra(
240 PreloadRequestReceiver.EXTRA_SEARCHBOX_SETQUERY);
Michael Kolb14612442011-06-24 13:06:29 -0700241 preloaded = Preloader.getInstance().getPreloadedTab(id);
242 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700243 } else if (Intent.ACTION_SEARCH.equals(action)
244 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
245 || Intent.ACTION_WEB_SEARCH.equals(action)) {
246 url = intent.getStringExtra(SearchManager.QUERY);
247 if (url != null) {
248 // In general, we shouldn't modify URL from Intent.
249 // But currently, we get the user-typed URL from search box as well.
250 url = UrlUtils.fixUrl(url);
251 url = UrlUtils.smartUrlFilter(url);
Michael Kolb8233fac2010-10-26 16:08:53 -0700252 String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&";
253 if (url.contains(searchSource)) {
254 String source = null;
255 final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
256 if (appData != null) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800257 source = appData.getString("source");
Michael Kolb8233fac2010-10-26 16:08:53 -0700258 }
259 if (TextUtils.isEmpty(source)) {
260 source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
261 }
262 url = url.replace(searchSource, "&source=android-"+source+"&");
263 }
264 }
265 }
266 }
Mathew Inwood9ad1eac2011-09-15 11:29:50 +0100267 return new UrlData(url, headers, intent, preloaded, preloadedSearchBoxQuery);
Michael Kolb8233fac2010-10-26 16:08:53 -0700268 }
269
270 /**
271 * Launches the default web search activity with the query parameters if the given intent's data
272 * are identified as plain search terms and not URLs/shortcuts.
273 * @return true if the intent was handled and web search activity was launched, false if not.
274 */
275 static boolean handleWebSearchIntent(Activity activity,
276 Controller controller, Intent intent) {
277 if (intent == null) return false;
278
279 String url = null;
280 final String action = intent.getAction();
Michael Kolb8233fac2010-10-26 16:08:53 -0700281 if (Intent.ACTION_VIEW.equals(action)) {
282 Uri data = intent.getData();
283 if (data != null) url = data.toString();
284 } else if (Intent.ACTION_SEARCH.equals(action)
285 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
286 || Intent.ACTION_WEB_SEARCH.equals(action)) {
287 url = intent.getStringExtra(SearchManager.QUERY);
288 }
289 return handleWebSearchRequest(activity, controller, url,
290 intent.getBundleExtra(SearchManager.APP_DATA),
291 intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
292 }
293
294 /**
295 * Launches the default web search activity with the query parameters if the given url string
296 * was identified as plain search terms and not URL/shortcut.
297 * @return true if the request was handled and web search activity was launched, false if not.
298 */
299 private static boolean handleWebSearchRequest(Activity activity,
300 Controller controller, String inUrl, Bundle appData,
301 String extraData) {
John Reck99141272010-12-21 11:34:12 -0800302 if (inUrl == null) return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700303
304 // In general, we shouldn't modify URL from Intent.
305 // But currently, we get the user-typed URL from search box as well.
306 String url = UrlUtils.fixUrl(inUrl).trim();
John Reck99141272010-12-21 11:34:12 -0800307 if (TextUtils.isEmpty(url)) return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700308
309 // URLs are handled by the regular flow of control, so
310 // return early.
311 if (Patterns.WEB_URL.matcher(url).matches()
312 || UrlUtils.ACCEPTED_URI_SCHEMA.matcher(url).matches()) {
313 return false;
314 }
315
316 final ContentResolver cr = activity.getContentResolver();
317 final String newUrl = url;
318 if (controller == null || controller.getTabControl() == null
319 || controller.getTabControl().getCurrentWebView() == null
320 || !controller.getTabControl().getCurrentWebView()
321 .isPrivateBrowsingEnabled()) {
322 new AsyncTask<Void, Void, Void>() {
323 @Override
324 protected Void doInBackground(Void... unused) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700325 Browser.addSearchUrl(cr, newUrl);
326 return null;
327 }
328 }.execute();
329 }
330
331 SearchEngine searchEngine = BrowserSettings.getInstance().getSearchEngine();
332 if (searchEngine == null) return false;
333 searchEngine.startSearch(activity, url, appData, extraData);
334
335 return true;
336 }
337
338 /**
339 * A UrlData class to abstract how the content will be set to WebView.
340 * This base class uses loadUrl to show the content.
341 */
342 static class UrlData {
343 final String mUrl;
344 final Map<String, String> mHeaders;
Mathew Inwood29721c22011-06-29 17:55:24 +0100345 final PreloadedTabControl mPreloadedTab;
346 final String mSearchBoxQueryToSubmit;
John Reck38b39652012-06-05 09:22:59 -0700347 final boolean mDisableUrlOverride;
Michael Kolb8233fac2010-10-26 16:08:53 -0700348
349 UrlData(String url) {
350 this.mUrl = url;
351 this.mHeaders = null;
Michael Kolb14612442011-06-24 13:06:29 -0700352 this.mPreloadedTab = null;
Mathew Inwood29721c22011-06-29 17:55:24 +0100353 this.mSearchBoxQueryToSubmit = null;
John Reck38b39652012-06-05 09:22:59 -0700354 this.mDisableUrlOverride = false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700355 }
356
357 UrlData(String url, Map<String, String> headers, Intent intent) {
Mathew Inwood9ad1eac2011-09-15 11:29:50 +0100358 this(url, headers, intent, null, null);
Michael Kolb14612442011-06-24 13:06:29 -0700359 }
360
Mathew Inwood29721c22011-06-29 17:55:24 +0100361 UrlData(String url, Map<String, String> headers, Intent intent,
Mathew Inwood9ad1eac2011-09-15 11:29:50 +0100362 PreloadedTabControl preloaded, String searchBoxQueryToSubmit) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700363 this.mUrl = url;
364 this.mHeaders = headers;
Mathew Inwood29721c22011-06-29 17:55:24 +0100365 this.mPreloadedTab = preloaded;
366 this.mSearchBoxQueryToSubmit = searchBoxQueryToSubmit;
John Reck38b39652012-06-05 09:22:59 -0700367 if (intent != null) {
368 mDisableUrlOverride = intent.getBooleanExtra(
369 BrowserActivity.EXTRA_DISABLE_URL_OVERRIDE, false);
370 } else {
371 mDisableUrlOverride = false;
372 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700373 }
374
375 boolean isEmpty() {
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700376 return (mUrl == null || mUrl.length() == 0);
Michael Kolb8233fac2010-10-26 16:08:53 -0700377 }
Michael Kolb14612442011-06-24 13:06:29 -0700378
379 boolean isPreloaded() {
380 return mPreloadedTab != null;
381 }
382
Mathew Inwood29721c22011-06-29 17:55:24 +0100383 PreloadedTabControl getPreloadedTab() {
Michael Kolb14612442011-06-24 13:06:29 -0700384 return mPreloadedTab;
385 }
Mathew Inwood29721c22011-06-29 17:55:24 +0100386
387 String getSearchBoxQueryToSubmit() {
388 return mSearchBoxQueryToSubmit;
389 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700390 }
391
392}