blob: f829ae1fcbcd847579e2bfbf21c9783a1bc1ced0 [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;
29import android.provider.Browser;
30import android.provider.MediaStore;
Michael Kolb8233fac2010-10-26 16:08:53 -070031import android.text.TextUtils;
32import android.util.Patterns;
33
Bijan Amirzada41242f22014-03-21 12:12:18 -070034import com.android.browser.UI.ComboViews;
35import 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) {
66 Tab current = mTabControl.getCurrentTab();
67 // When a tab is closed on exit, the current tab index is set to -1.
68 // Reset before proceed as Browser requires the current tab to be set.
69 if (current == null) {
70 // Try to reset the tab in case the index was incorrect.
71 current = mTabControl.getTab(0);
72 if (current == null) {
73 // No tabs at all so just ignore this intent.
74 return;
75 }
76 mController.setActiveTab(current);
Michael Kolb8233fac2010-10-26 16:08:53 -070077 }
78 final String action = intent.getAction();
79 final int flags = intent.getFlags();
80 if (Intent.ACTION_MAIN.equals(action) ||
81 (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) {
82 // just resume the browser
83 return;
84 }
John Reck439c9a52010-12-14 10:04:39 -080085 if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(action)) {
Michael Kolb315d5022011-10-13 12:47:11 -070086 mController.bookmarksOrHistoryPicker(ComboViews.Bookmarks);
John Reck439c9a52010-12-14 10:04:39 -080087 return;
88 }
John Reck07af2b82011-01-18 14:24:43 -080089
Michael Kolb8233fac2010-10-26 16:08:53 -070090 // In case the SearchDialog is open.
91 ((SearchManager) mActivity.getSystemService(Context.SEARCH_SERVICE))
92 .stopSearch();
Michael Kolb8233fac2010-10-26 16:08:53 -070093 if (Intent.ACTION_VIEW.equals(action)
Jeff Hamiltonfd77aaa2011-06-17 16:19:33 -050094 || NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)
Michael Kolb8233fac2010-10-26 16:08:53 -070095 || Intent.ACTION_SEARCH.equals(action)
96 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
Michael Kolb5ff5c8b2012-05-03 11:37:58 -070097 || Intent.ACTION_WEB_SEARCH.equals(action)) {
Michael Kolb8233fac2010-10-26 16:08:53 -070098 // If this was a search request (e.g. search query directly typed into the address bar),
99 // pass it on to the default web search provider.
100 if (handleWebSearchIntent(mActivity, mController, intent)) {
101 return;
102 }
103
104 UrlData urlData = getUrlDataFromIntent(intent);
105 if (urlData.isEmpty()) {
106 urlData = new UrlData(mSettings.getHomePage());
107 }
108
Michael Kolb14612442011-06-24 13:06:29 -0700109 if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)
110 || urlData.isPreloaded()) {
111 Tab t = mController.openTab(urlData);
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700112 t.setDerivedFromIntent(true);
Leon Scroggins2ed6e312011-02-22 16:37:23 -0500113 return;
114 }
John Reckdb22ec42011-06-29 11:31:24 -0700115 /*
116 * TODO: Don't allow javascript URIs
117 * 0) If this is a javascript: URI, *always* open a new tab
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700118 * 1) If the URL is already opened, switch to that tab
119 * 2-phone) Reuse tab with same appId
120 * 2-tablet) Open new tab
John Reckdb22ec42011-06-29 11:31:24 -0700121 */
Michael Kolb8233fac2010-10-26 16:08:53 -0700122 final String appId = intent
123 .getStringExtra(Browser.EXTRA_APPLICATION_ID);
John Reck26b18322011-06-21 13:08:58 -0700124 if (!TextUtils.isEmpty(urlData.mUrl) &&
125 urlData.mUrl.startsWith("javascript:")) {
126 // Always open javascript: URIs in new tabs
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700127 Tab jsTab = mController.openTab(urlData);
128 jsTab.setDerivedFromIntent(true);
John Reck26b18322011-06-21 13:08:58 -0700129 return;
130 }
Michael Kolbf1286a42012-04-17 14:10:52 -0700131 if (Intent.ACTION_VIEW.equals(action)
132 && (appId != null)
133 && appId.startsWith(mActivity.getPackageName())) {
134 Tab appTab = mTabControl.getTabFromAppId(appId);
135 if ((appTab != null) && (appTab == mController.getCurrentTab())) {
136 mController.switchToTab(appTab);
137 mController.loadUrlDataIn(appTab, urlData);
138 return;
139 }
140 }
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700141 if (Intent.ACTION_VIEW.equals(action)
142 && !mActivity.getPackageName().equals(appId)) {
Michael Kolb8d772b02012-01-19 13:56:00 -0800143 if (!BrowserActivity.isTablet(mActivity)
144 && !mSettings.allowAppTabs()) {
Michael Kolbc831b632011-05-11 09:30:34 -0700145 Tab appTab = mTabControl.getTabFromAppId(appId);
Michael Kolbaf0d3342011-03-11 11:30:16 -0800146 if (appTab != null) {
John Reck26b18322011-06-21 13:08:58 -0700147 mController.reuseTab(appTab, urlData);
Michael Kolbaf0d3342011-03-11 11:30:16 -0800148 return;
Michael Kolbaf0d3342011-03-11 11:30:16 -0800149 }
John Reckdb22ec42011-06-29 11:31:24 -0700150 }
151 // No matching application tab, try to find a regular tab
152 // with a matching url.
153 Tab appTab = mTabControl.findTabWithUrl(urlData.mUrl);
154 if (appTab != null) {
155 // Transfer ownership
156 appTab.setAppId(appId);
157 if (current != appTab) {
158 mController.switchToTab(appTab);
159 }
160 // Otherwise, we are already viewing the correct tab.
Michael Kolb8233fac2010-10-26 16:08:53 -0700161 } else {
John Reckdb22ec42011-06-29 11:31:24 -0700162 // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url
163 // will be opened in a new tab unless we have reached
164 // MAX_TABS. Then the url will be opened in the current
165 // tab. If a new tab is created, it will have "true" for
166 // exit on close.
167 Tab tab = mController.openTab(urlData);
168 if (tab != null) {
169 tab.setAppId(appId);
Axesh R. Ajmera2e241242014-05-19 15:53:38 -0700170 tab.setDerivedFromIntent(true);
Michael Kolbe28b3472011-08-04 16:54:31 -0700171 if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
172 tab.setCloseOnBack(true);
173 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700174 }
175 }
176 } else {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800177 if (!urlData.isEmpty()
Michael Kolb8233fac2010-10-26 16:08:53 -0700178 && urlData.mUrl.startsWith("about:debug")) {
179 if ("about:debug.dom".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800180 current.getWebView().dumpDomTree(false);
Michael Kolb8233fac2010-10-26 16:08:53 -0700181 } else if ("about:debug.dom.file".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800182 current.getWebView().dumpDomTree(true);
Michael Kolb8233fac2010-10-26 16:08:53 -0700183 } else if ("about:debug.render".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800184 current.getWebView().dumpRenderTree(false);
Michael Kolb8233fac2010-10-26 16:08:53 -0700185 } else if ("about:debug.render.file".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800186 current.getWebView().dumpRenderTree(true);
Michael Kolb8233fac2010-10-26 16:08:53 -0700187 } else if ("about:debug.display".equals(urlData.mUrl)) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800188 current.getWebView().dumpDisplayTree();
Cary Clark4f4cb6b2011-01-18 13:03:42 -0500189 } else if ("about:debug.nav".equals(urlData.mUrl)) {
190 current.getWebView().debugDump();
Michael Kolb8233fac2010-10-26 16:08:53 -0700191 } else {
John Reck35e9dd62011-04-25 09:01:54 -0700192 mSettings.toggleDebugSettings();
Michael Kolb8233fac2010-10-26 16:08:53 -0700193 }
194 return;
195 }
196 // Get rid of the subwindow if it exists
197 mController.dismissSubWindow(current);
198 // If the current Tab is being used as an application tab,
199 // remove the association, since the new Intent means that it is
200 // no longer associated with that application.
201 current.setAppId(null);
202 mController.loadUrlDataIn(current, urlData);
203 }
204 }
205 }
206
Michael Kolb14612442011-06-24 13:06:29 -0700207 protected static UrlData getUrlDataFromIntent(Intent intent) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700208 String url = "";
209 Map<String, String> headers = null;
Mathew Inwood29721c22011-06-29 17:55:24 +0100210 PreloadedTabControl preloaded = null;
211 String preloadedSearchBoxQuery = null;
Leon Scrogginse1cab102011-01-04 10:50:13 -0500212 if (intent != null
213 && (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700214 final String action = intent.getAction();
Jeff Hamiltonfd77aaa2011-06-17 16:19:33 -0500215 if (Intent.ACTION_VIEW.equals(action) ||
216 NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700217 url = UrlUtils.smartUrlFilter(intent.getData());
Michael Kolb8233fac2010-10-26 16:08:53 -0700218 if (url != null && url.startsWith("http")) {
219 final Bundle pairs = intent
220 .getBundleExtra(Browser.EXTRA_HEADERS);
221 if (pairs != null && !pairs.isEmpty()) {
222 Iterator<String> iter = pairs.keySet().iterator();
223 headers = new HashMap<String, String>();
224 while (iter.hasNext()) {
225 String key = iter.next();
226 headers.put(key, pairs.getString(key));
227 }
228 }
229 }
Michael Kolb14612442011-06-24 13:06:29 -0700230 if (intent.hasExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID)) {
231 String id = intent.getStringExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID);
Mathew Inwood29721c22011-06-29 17:55:24 +0100232 preloadedSearchBoxQuery = intent.getStringExtra(
233 PreloadRequestReceiver.EXTRA_SEARCHBOX_SETQUERY);
Michael Kolb14612442011-06-24 13:06:29 -0700234 preloaded = Preloader.getInstance().getPreloadedTab(id);
235 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700236 } else if (Intent.ACTION_SEARCH.equals(action)
237 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
238 || Intent.ACTION_WEB_SEARCH.equals(action)) {
239 url = intent.getStringExtra(SearchManager.QUERY);
240 if (url != null) {
241 // In general, we shouldn't modify URL from Intent.
242 // But currently, we get the user-typed URL from search box as well.
243 url = UrlUtils.fixUrl(url);
244 url = UrlUtils.smartUrlFilter(url);
Michael Kolb8233fac2010-10-26 16:08:53 -0700245 String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&";
246 if (url.contains(searchSource)) {
247 String source = null;
248 final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
249 if (appData != null) {
Bijan Amirzada9b1e9882014-02-26 17:15:46 -0800250 source = appData.getString("source");
Michael Kolb8233fac2010-10-26 16:08:53 -0700251 }
252 if (TextUtils.isEmpty(source)) {
253 source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
254 }
255 url = url.replace(searchSource, "&source=android-"+source+"&");
256 }
257 }
258 }
259 }
Mathew Inwood9ad1eac2011-09-15 11:29:50 +0100260 return new UrlData(url, headers, intent, preloaded, preloadedSearchBoxQuery);
Michael Kolb8233fac2010-10-26 16:08:53 -0700261 }
262
263 /**
264 * Launches the default web search activity with the query parameters if the given intent's data
265 * are identified as plain search terms and not URLs/shortcuts.
266 * @return true if the intent was handled and web search activity was launched, false if not.
267 */
268 static boolean handleWebSearchIntent(Activity activity,
269 Controller controller, Intent intent) {
270 if (intent == null) return false;
271
272 String url = null;
273 final String action = intent.getAction();
Michael Kolb8233fac2010-10-26 16:08:53 -0700274 if (Intent.ACTION_VIEW.equals(action)) {
275 Uri data = intent.getData();
276 if (data != null) url = data.toString();
277 } else if (Intent.ACTION_SEARCH.equals(action)
278 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
279 || Intent.ACTION_WEB_SEARCH.equals(action)) {
280 url = intent.getStringExtra(SearchManager.QUERY);
281 }
282 return handleWebSearchRequest(activity, controller, url,
283 intent.getBundleExtra(SearchManager.APP_DATA),
284 intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
285 }
286
287 /**
288 * Launches the default web search activity with the query parameters if the given url string
289 * was identified as plain search terms and not URL/shortcut.
290 * @return true if the request was handled and web search activity was launched, false if not.
291 */
292 private static boolean handleWebSearchRequest(Activity activity,
293 Controller controller, String inUrl, Bundle appData,
294 String extraData) {
John Reck99141272010-12-21 11:34:12 -0800295 if (inUrl == null) return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700296
297 // In general, we shouldn't modify URL from Intent.
298 // But currently, we get the user-typed URL from search box as well.
299 String url = UrlUtils.fixUrl(inUrl).trim();
John Reck99141272010-12-21 11:34:12 -0800300 if (TextUtils.isEmpty(url)) return false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700301
302 // URLs are handled by the regular flow of control, so
303 // return early.
304 if (Patterns.WEB_URL.matcher(url).matches()
305 || UrlUtils.ACCEPTED_URI_SCHEMA.matcher(url).matches()) {
306 return false;
307 }
308
309 final ContentResolver cr = activity.getContentResolver();
310 final String newUrl = url;
311 if (controller == null || controller.getTabControl() == null
312 || controller.getTabControl().getCurrentWebView() == null
313 || !controller.getTabControl().getCurrentWebView()
314 .isPrivateBrowsingEnabled()) {
315 new AsyncTask<Void, Void, Void>() {
316 @Override
317 protected Void doInBackground(Void... unused) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700318 Browser.addSearchUrl(cr, newUrl);
319 return null;
320 }
321 }.execute();
322 }
323
324 SearchEngine searchEngine = BrowserSettings.getInstance().getSearchEngine();
325 if (searchEngine == null) return false;
326 searchEngine.startSearch(activity, url, appData, extraData);
327
328 return true;
329 }
330
331 /**
332 * A UrlData class to abstract how the content will be set to WebView.
333 * This base class uses loadUrl to show the content.
334 */
335 static class UrlData {
336 final String mUrl;
337 final Map<String, String> mHeaders;
Mathew Inwood29721c22011-06-29 17:55:24 +0100338 final PreloadedTabControl mPreloadedTab;
339 final String mSearchBoxQueryToSubmit;
John Reck38b39652012-06-05 09:22:59 -0700340 final boolean mDisableUrlOverride;
Michael Kolb8233fac2010-10-26 16:08:53 -0700341
342 UrlData(String url) {
343 this.mUrl = url;
344 this.mHeaders = null;
Michael Kolb14612442011-06-24 13:06:29 -0700345 this.mPreloadedTab = null;
Mathew Inwood29721c22011-06-29 17:55:24 +0100346 this.mSearchBoxQueryToSubmit = null;
John Reck38b39652012-06-05 09:22:59 -0700347 this.mDisableUrlOverride = false;
Michael Kolb8233fac2010-10-26 16:08:53 -0700348 }
349
350 UrlData(String url, Map<String, String> headers, Intent intent) {
Mathew Inwood9ad1eac2011-09-15 11:29:50 +0100351 this(url, headers, intent, null, null);
Michael Kolb14612442011-06-24 13:06:29 -0700352 }
353
Mathew Inwood29721c22011-06-29 17:55:24 +0100354 UrlData(String url, Map<String, String> headers, Intent intent,
Mathew Inwood9ad1eac2011-09-15 11:29:50 +0100355 PreloadedTabControl preloaded, String searchBoxQueryToSubmit) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700356 this.mUrl = url;
357 this.mHeaders = headers;
Mathew Inwood29721c22011-06-29 17:55:24 +0100358 this.mPreloadedTab = preloaded;
359 this.mSearchBoxQueryToSubmit = searchBoxQueryToSubmit;
John Reck38b39652012-06-05 09:22:59 -0700360 if (intent != null) {
361 mDisableUrlOverride = intent.getBooleanExtra(
362 BrowserActivity.EXTRA_DISABLE_URL_OVERRIDE, false);
363 } else {
364 mDisableUrlOverride = false;
365 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700366 }
367
368 boolean isEmpty() {
Michael Kolb5ff5c8b2012-05-03 11:37:58 -0700369 return (mUrl == null || mUrl.length() == 0);
Michael Kolb8233fac2010-10-26 16:08:53 -0700370 }
Michael Kolb14612442011-06-24 13:06:29 -0700371
372 boolean isPreloaded() {
373 return mPreloadedTab != null;
374 }
375
Mathew Inwood29721c22011-06-29 17:55:24 +0100376 PreloadedTabControl getPreloadedTab() {
Michael Kolb14612442011-06-24 13:06:29 -0700377 return mPreloadedTab;
378 }
Mathew Inwood29721c22011-06-29 17:55:24 +0100379
380 String getSearchBoxQueryToSubmit() {
381 return mSearchBoxQueryToSubmit;
382 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700383 }
384
385}