• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)
111                   || urlData.isPreloaded()) {
112                 Tab t = mController.openTab(urlData);
113                 return;
114             }
115             /*
116              * TODO: Don't allow javascript URIs
117              * 0) If this is a javascript: URI, *always* open a new tab
118              * 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
121              */
122             final String appId = intent
123                     .getStringExtra(Browser.EXTRA_APPLICATION_ID);
124             if (!TextUtils.isEmpty(urlData.mUrl) &&
125                     urlData.mUrl.startsWith("javascript:")) {
126                 // Always open javascript: URIs in new tabs
127                 mController.openTab(urlData);
128                 return;
129             }
130             if (Intent.ACTION_VIEW.equals(action)
131                     && (appId != null)
132                     && appId.startsWith(mActivity.getPackageName())) {
133                 Tab appTab = mTabControl.getTabFromAppId(appId);
134                 if ((appTab != null) && (appTab == mController.getCurrentTab())) {
135                     mController.switchToTab(appTab);
136                     mController.loadUrlDataIn(appTab, urlData);
137                     return;
138                 }
139             }
140             if (Intent.ACTION_VIEW.equals(action)
141                      && !mActivity.getPackageName().equals(appId)) {
142                 if (!BrowserActivity.isTablet(mActivity)
143                         && !mSettings.allowAppTabs()) {
144                     Tab appTab = mTabControl.getTabFromAppId(appId);
145                     if (appTab != null) {
146                         mController.reuseTab(appTab, urlData);
147                         return;
148                     }
149                 }
150                 // No matching application tab, try to find a regular tab
151                 // with a matching url.
152                 Tab appTab = mTabControl.findTabWithUrl(urlData.mUrl);
153                 if (appTab != null) {
154                     // Transfer ownership
155                     appTab.setAppId(appId);
156                     if (current != appTab) {
157                         mController.switchToTab(appTab);
158                     }
159                     // Otherwise, we are already viewing the correct tab.
160                 } else {
161                     // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url
162                     // will be opened in a new tab unless we have reached
163                     // MAX_TABS. Then the url will be opened in the current
164                     // tab. If a new tab is created, it will have "true" for
165                     // exit on close.
166                     Tab tab = mController.openTab(urlData);
167                     if (tab != null) {
168                         tab.setAppId(appId);
169                         if ((intent.getFlags() & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
170                             tab.setCloseOnBack(true);
171                         }
172                     }
173                 }
174             } else {
175                 if (!urlData.isEmpty()
176                         && urlData.mUrl.startsWith("about:debug")) {
177                     if ("about:debug.dom".equals(urlData.mUrl)) {
178                         current.getWebViewClassic().dumpDomTree(false);
179                     } else if ("about:debug.dom.file".equals(urlData.mUrl)) {
180                         current.getWebViewClassic().dumpDomTree(true);
181                     } else if ("about:debug.render".equals(urlData.mUrl)) {
182                         current.getWebViewClassic().dumpRenderTree(false);
183                     } else if ("about:debug.render.file".equals(urlData.mUrl)) {
184                         current.getWebViewClassic().dumpRenderTree(true);
185                     } else if ("about:debug.display".equals(urlData.mUrl)) {
186                         current.getWebViewClassic().dumpDisplayTree();
187                     } else if ("about:debug.nav".equals(urlData.mUrl)) {
188                         current.getWebView().debugDump();
189                     } else {
190                         mSettings.toggleDebugSettings();
191                     }
192                     return;
193                 }
194                 // Get rid of the subwindow if it exists
195                 mController.dismissSubWindow(current);
196                 // If the current Tab is being used as an application tab,
197                 // remove the association, since the new Intent means that it is
198                 // no longer associated with that application.
199                 current.setAppId(null);
200                 mController.loadUrlDataIn(current, urlData);
201             }
202         }
203     }
204 
getUrlDataFromIntent(Intent intent)205     protected static UrlData getUrlDataFromIntent(Intent intent) {
206         String url = "";
207         Map<String, String> headers = null;
208         PreloadedTabControl preloaded = null;
209         String preloadedSearchBoxQuery = null;
210         if (intent != null
211                 && (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
212             final String action = intent.getAction();
213             if (Intent.ACTION_VIEW.equals(action) ||
214                     NfcAdapter.ACTION_NDEF_DISCOVERED.equals(action)) {
215                 url = UrlUtils.smartUrlFilter(intent.getData());
216                 if (url != null && url.startsWith("http")) {
217                     final Bundle pairs = intent
218                             .getBundleExtra(Browser.EXTRA_HEADERS);
219                     if (pairs != null && !pairs.isEmpty()) {
220                         Iterator<String> iter = pairs.keySet().iterator();
221                         headers = new HashMap<String, String>();
222                         while (iter.hasNext()) {
223                             String key = iter.next();
224                             headers.put(key, pairs.getString(key));
225                         }
226                     }
227                 }
228                 if (intent.hasExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID)) {
229                     String id = intent.getStringExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID);
230                     preloadedSearchBoxQuery = intent.getStringExtra(
231                             PreloadRequestReceiver.EXTRA_SEARCHBOX_SETQUERY);
232                     preloaded = Preloader.getInstance().getPreloadedTab(id);
233                 }
234             } else if (Intent.ACTION_SEARCH.equals(action)
235                     || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
236                     || Intent.ACTION_WEB_SEARCH.equals(action)) {
237                 url = intent.getStringExtra(SearchManager.QUERY);
238                 if (url != null) {
239                     // In general, we shouldn't modify URL from Intent.
240                     // But currently, we get the user-typed URL from search box as well.
241                     url = UrlUtils.fixUrl(url);
242                     url = UrlUtils.smartUrlFilter(url);
243                     String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&";
244                     if (url.contains(searchSource)) {
245                         String source = null;
246                         final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
247                         if (appData != null) {
248                             source = appData.getString(Search.SOURCE);
249                         }
250                         if (TextUtils.isEmpty(source)) {
251                             source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
252                         }
253                         url = url.replace(searchSource, "&source=android-"+source+"&");
254                     }
255                 }
256             }
257         }
258         return new UrlData(url, headers, intent, preloaded, preloadedSearchBoxQuery);
259     }
260 
261     /**
262      * Launches the default web search activity with the query parameters if the given intent's data
263      * are identified as plain search terms and not URLs/shortcuts.
264      * @return true if the intent was handled and web search activity was launched, false if not.
265      */
handleWebSearchIntent(Activity activity, Controller controller, Intent intent)266     static boolean handleWebSearchIntent(Activity activity,
267             Controller controller, Intent intent) {
268         if (intent == null) return false;
269 
270         String url = null;
271         final String action = intent.getAction();
272         if (Intent.ACTION_VIEW.equals(action)) {
273             Uri data = intent.getData();
274             if (data != null) url = data.toString();
275         } else if (Intent.ACTION_SEARCH.equals(action)
276                 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
277                 || Intent.ACTION_WEB_SEARCH.equals(action)) {
278             url = intent.getStringExtra(SearchManager.QUERY);
279         }
280         return handleWebSearchRequest(activity, controller, url,
281                 intent.getBundleExtra(SearchManager.APP_DATA),
282                 intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
283     }
284 
285     /**
286      * Launches the default web search activity with the query parameters if the given url string
287      * was identified as plain search terms and not URL/shortcut.
288      * @return true if the request was handled and web search activity was launched, false if not.
289      */
handleWebSearchRequest(Activity activity, Controller controller, String inUrl, Bundle appData, String extraData)290     private static boolean handleWebSearchRequest(Activity activity,
291             Controller controller, String inUrl, Bundle appData,
292             String extraData) {
293         if (inUrl == null) return false;
294 
295         // In general, we shouldn't modify URL from Intent.
296         // But currently, we get the user-typed URL from search box as well.
297         String url = UrlUtils.fixUrl(inUrl).trim();
298         if (TextUtils.isEmpty(url)) return false;
299 
300         // URLs are handled by the regular flow of control, so
301         // return early.
302         if (Patterns.WEB_URL.matcher(url).matches()
303                 || UrlUtils.ACCEPTED_URI_SCHEMA.matcher(url).matches()) {
304             return false;
305         }
306 
307         final ContentResolver cr = activity.getContentResolver();
308         final String newUrl = url;
309         if (controller == null || controller.getTabControl() == null
310                 || controller.getTabControl().getCurrentWebView() == null
311                 || !controller.getTabControl().getCurrentWebView()
312                 .isPrivateBrowsingEnabled()) {
313             new AsyncTask<Void, Void, Void>() {
314                 @Override
315                 protected Void doInBackground(Void... unused) {
316                         Browser.addSearchUrl(cr, newUrl);
317                     return null;
318                 }
319             }.execute();
320         }
321 
322         SearchEngine searchEngine = BrowserSettings.getInstance().getSearchEngine();
323         if (searchEngine == null) return false;
324         searchEngine.startSearch(activity, url, appData, extraData);
325 
326         return true;
327     }
328 
329     /**
330      * A UrlData class to abstract how the content will be set to WebView.
331      * This base class uses loadUrl to show the content.
332      */
333     static class UrlData {
334         final String mUrl;
335         final Map<String, String> mHeaders;
336         final PreloadedTabControl mPreloadedTab;
337         final String mSearchBoxQueryToSubmit;
338         final boolean mDisableUrlOverride;
339 
UrlData(String url)340         UrlData(String url) {
341             this.mUrl = url;
342             this.mHeaders = null;
343             this.mPreloadedTab = null;
344             this.mSearchBoxQueryToSubmit = null;
345             this.mDisableUrlOverride = false;
346         }
347 
UrlData(String url, Map<String, String> headers, Intent intent)348         UrlData(String url, Map<String, String> headers, Intent intent) {
349             this(url, headers, intent, null, null);
350         }
351 
UrlData(String url, Map<String, String> headers, Intent intent, PreloadedTabControl preloaded, String searchBoxQueryToSubmit)352         UrlData(String url, Map<String, String> headers, Intent intent,
353                 PreloadedTabControl preloaded, String searchBoxQueryToSubmit) {
354             this.mUrl = url;
355             this.mHeaders = headers;
356             this.mPreloadedTab = preloaded;
357             this.mSearchBoxQueryToSubmit = searchBoxQueryToSubmit;
358             if (intent != null) {
359                 mDisableUrlOverride = intent.getBooleanExtra(
360                         BrowserActivity.EXTRA_DISABLE_URL_OVERRIDE, false);
361             } else {
362                 mDisableUrlOverride = false;
363             }
364         }
365 
isEmpty()366         boolean isEmpty() {
367             return (mUrl == null || mUrl.length() == 0);
368         }
369 
isPreloaded()370         boolean isPreloaded() {
371             return mPreloadedTab != null;
372         }
373 
getPreloadedTab()374         PreloadedTabControl getPreloadedTab() {
375             return mPreloadedTab;
376         }
377 
getSearchBoxQueryToSubmit()378         String getSearchBoxQueryToSubmit() {
379             return mSearchBoxQueryToSubmit;
380         }
381     }
382 
383 }
384