blob: 37480afdf3d440096a082941e31908da29eb1b82 [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
Bijan Amirzada41242f22014-03-21 12:12:18 -070017package com.android.browser;
Michael Kolb8233fac2010-10-26 16:08:53 -070018
19import android.app.Activity;
20import android.content.ActivityNotFoundException;
21import android.content.Intent;
John Reck95a49ff2011-02-08 18:23:22 -080022import android.content.IntentFilter;
Michael Kolb8233fac2010-10-26 16:08:53 -070023import android.content.pm.PackageManager;
John Reck95a49ff2011-02-08 18:23:22 -080024import android.content.pm.ResolveInfo;
Michael Kolb8233fac2010-10-26 16:08:53 -070025import android.net.Uri;
John Reck8bcafc12011-08-29 16:43:02 -070026import android.provider.Browser;
Michael Kolb8233fac2010-10-26 16:08:53 -070027import android.util.Log;
kaiyizc4ada322013-07-30 09:58:07 +080028import android.widget.Toast;
Michael Kolb8233fac2010-10-26 16:08:53 -070029
Bijan Amirzada41242f22014-03-21 12:12:18 -070030import com.android.browser.R;
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080031
Michael Kolb8233fac2010-10-26 16:08:53 -070032import java.net.URISyntaxException;
John Reck95a49ff2011-02-08 18:23:22 -080033import java.util.List;
John Reckdb3d43d2011-02-11 11:56:38 -080034import java.util.regex.Matcher;
Michael Kolb8233fac2010-10-26 16:08:53 -070035
Bijan Amirzada9b1e9882014-02-26 17:15:46 -080036import org.codeaurora.swe.WebView;
37
Michael Kolb8233fac2010-10-26 16:08:53 -070038public class UrlHandler {
39
kaiyizc4ada322013-07-30 09:58:07 +080040 private final static String TAG = "UrlHandler";
John Reck35e9dd62011-04-25 09:01:54 -070041
Michael Kolb8233fac2010-10-26 16:08:53 -070042 // Use in overrideUrlLoading
43 /* package */ final static String SCHEME_WTAI = "wtai://wp/";
44 /* package */ final static String SCHEME_WTAI_MC = "wtai://wp/mc;";
45 /* package */ final static String SCHEME_WTAI_SD = "wtai://wp/sd;";
46 /* package */ final static String SCHEME_WTAI_AP = "wtai://wp/ap;";
Vivek Sekharb54614f2014-05-01 19:03:37 -070047 /* package */ final static String SCHEME_MAILTO = "mailto:";
Michael Kolb8233fac2010-10-26 16:08:53 -070048 Controller mController;
49 Activity mActivity;
50
Michael Kolb8233fac2010-10-26 16:08:53 -070051 public UrlHandler(Controller controller) {
52 mController = controller;
53 mActivity = mController.getActivity();
54 }
55
Michael Kolb18eb3772010-12-10 14:29:51 -080056 boolean shouldOverrideUrlLoading(Tab tab, WebView view, String url) {
Michael Kolb8233fac2010-10-26 16:08:53 -070057 if (view.isPrivateBrowsingEnabled()) {
58 // Don't allow urls to leave the browser app when in
59 // private browsing mode
Patrick Scottd05faa62010-12-16 09:15:34 -050060 return false;
Michael Kolb8233fac2010-10-26 16:08:53 -070061 }
62
63 if (url.startsWith(SCHEME_WTAI)) {
64 // wtai://wp/mc;number
65 // number=string(phone-number)
66 if (url.startsWith(SCHEME_WTAI_MC)) {
67 Intent intent = new Intent(Intent.ACTION_VIEW,
68 Uri.parse(WebView.SCHEME_TEL +
69 url.substring(SCHEME_WTAI_MC.length())));
70 mActivity.startActivity(intent);
71 // before leaving BrowserActivity, close the empty child tab.
72 // If a new tab is created through JavaScript open to load this
73 // url, we would like to close it as we will load this url in a
74 // different Activity.
John Reck8bcafc12011-08-29 16:43:02 -070075 mController.closeEmptyTab();
Michael Kolb8233fac2010-10-26 16:08:53 -070076 return true;
77 }
78 // wtai://wp/sd;dtmf
79 // dtmf=string(dialstring)
80 if (url.startsWith(SCHEME_WTAI_SD)) {
81 // TODO: only send when there is active voice connection
82 return false;
83 }
84 // wtai://wp/ap;number;name
85 // number=string(phone-number)
86 // name=string
87 if (url.startsWith(SCHEME_WTAI_AP)) {
88 // TODO
89 return false;
90 }
91 }
92
93 // The "about:" schemes are internal to the browser; don't want these to
94 // be dispatched to other apps.
95 if (url.startsWith("about:")) {
96 return false;
97 }
98
kaiyiz6e5b3e02013-08-19 20:02:01 +080099 if (url.startsWith("ae://") && url.endsWith("add-fav")) {
100 mController.startAddMyNavigation(url);
101 return true;
102 }
Michael Kolb8233fac2010-10-26 16:08:53 -0700103
Vivek Sekharb54614f2014-05-01 19:03:37 -0700104 // add for carrier feature - recognize additional website format
105 // here add to support "mailto:" scheme
106 if (url.startsWith(SCHEME_MAILTO) && handleMailtoTypeUrl(url)) {
107 return true;
108 }
109
110 // add for carrier feature - wap2estore
Bijan Amirzadae75909d2014-05-06 14:18:54 -0700111 boolean wap2estore =
112 mActivity.getApplicationContext().getResources().getBoolean(R.bool.wap2estore);
Vivek Sekharb54614f2014-05-01 19:03:37 -0700113 if (wap2estore && isEstoreTypeUrl(url) && handleEstoreTypeUrl(url)) {
kaiyizc4ada322013-07-30 09:58:07 +0800114 return true;
115 }
116
John Reck8bcafc12011-08-29 16:43:02 -0700117 if (startActivityForUrl(tab, url)) {
Russell Brennerd4afde12011-01-07 11:09:36 -0800118 return true;
Michael Kolb8233fac2010-10-26 16:08:53 -0700119 }
120
Russell Brenner14e1ae22011-01-12 14:54:23 -0800121 if (handleMenuClick(tab, url)) {
Michael Kolb8233fac2010-10-26 16:08:53 -0700122 return true;
123 }
Russell Brennerd4afde12011-01-07 11:09:36 -0800124
Michael Kolb8233fac2010-10-26 16:08:53 -0700125 return false;
126 }
127
Vivek Sekharb54614f2014-05-01 19:03:37 -0700128 private boolean handleMailtoTypeUrl(String url) {
129 Intent intent;
130 // perform generic parsing of the URI to turn it into an Intent.
kaiyizc4ada322013-07-30 09:58:07 +0800131 try {
Vivek Sekharb54614f2014-05-01 19:03:37 -0700132 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
133 mActivity.startActivity(intent);
134 } catch (URISyntaxException ex) {
135 Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
136 return false;
137 } catch (ActivityNotFoundException ex) {
138 Log.w("Browser", "No Activity Found for " + url);
139 return false;
kaiyizc4ada322013-07-30 09:58:07 +0800140 }
Vivek Sekharb54614f2014-05-01 19:03:37 -0700141
142 return true;
143 }
144
145 private boolean isEstoreTypeUrl(String url) {
146 if (url != null && url.startsWith("estore:")) {
kaiyizc4ada322013-07-30 09:58:07 +0800147 return true;
148 }
149 return false;
150 }
151
Vivek Sekharb54614f2014-05-01 19:03:37 -0700152 private boolean handleEstoreTypeUrl(String url) {
153 if (url.getBytes().length > 256) {
kaiyizc4ada322013-07-30 09:58:07 +0800154 Toast.makeText(mActivity, R.string.estore_url_warning, Toast.LENGTH_LONG).show();
Vivek Sekharb54614f2014-05-01 19:03:37 -0700155 return false;
kaiyizc4ada322013-07-30 09:58:07 +0800156 }
Vivek Sekharb54614f2014-05-01 19:03:37 -0700157
158 Intent intent;
159 // perform generic parsing of the URI to turn it into an Intent.
kaiyizc4ada322013-07-30 09:58:07 +0800160 try {
Vivek Sekharb54614f2014-05-01 19:03:37 -0700161 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
kaiyizc4ada322013-07-30 09:58:07 +0800162 mActivity.startActivity(intent);
Vivek Sekharb54614f2014-05-01 19:03:37 -0700163 } catch (URISyntaxException ex) {
164 Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
165 return false;
kaiyizc4ada322013-07-30 09:58:07 +0800166 } catch (ActivityNotFoundException ex) {
167 String downloadUrl = mActivity.getResources().getString(R.string.estore_homepage);
168 mController.loadUrl(mController.getCurrentTab(), downloadUrl);
169 Toast.makeText(mActivity, R.string.download_estore_app, Toast.LENGTH_LONG).show();
170 }
Vivek Sekharb54614f2014-05-01 19:03:37 -0700171
172 return true;
kaiyizc4ada322013-07-30 09:58:07 +0800173 }
174
Vivek Sekharb54614f2014-05-01 19:03:37 -0700175
John Reck8bcafc12011-08-29 16:43:02 -0700176 boolean startActivityForUrl(Tab tab, String url) {
Russell Brennerd4afde12011-01-07 11:09:36 -0800177 Intent intent;
178 // perform generic parsing of the URI to turn it into an Intent.
179 try {
180 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
181 } catch (URISyntaxException ex) {
182 Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
183 return false;
184 }
185
186 // check whether the intent can be resolved. If not, we will see
187 // whether we can download it from the Market.
188 if (mActivity.getPackageManager().resolveActivity(intent, 0) == null) {
189 String packagename = intent.getPackage();
190 if (packagename != null) {
Vivek Sekhar40713382014-06-11 14:29:32 -0700191 try {
192 intent = new Intent(Intent.ACTION_VIEW, Uri
193 .parse("market://search?q=pname:" + packagename));
194 intent.addCategory(Intent.CATEGORY_BROWSABLE);
195 mActivity.startActivity(intent);
196 // before leaving BrowserActivity, close the empty child tab.
197 // If a new tab is created through JavaScript open to load this
198 // url, we would like to close it as we will load this url in a
199 // different Activity.
200 mController.closeEmptyTab();
201 return true;
202 } catch (ActivityNotFoundException e) {
203 Log.w(TAG, "Play store not found while searching for : " + packagename);
204 CharSequence alert = mActivity.getResources().getString(
205 R.string.msg_no_google_play);
206 Toast t = Toast.makeText(mActivity , alert, Toast.LENGTH_SHORT);
207 t.show();
208 return false;
209 }
Russell Brennerd4afde12011-01-07 11:09:36 -0800210 } else {
211 return false;
212 }
213 }
214
215 // sanitize the Intent, ensuring web pages can not bypass browser
216 // security (only access to BROWSABLE activities).
217 intent.addCategory(Intent.CATEGORY_BROWSABLE);
218 intent.setComponent(null);
John Reck8bcafc12011-08-29 16:43:02 -0700219 // Re-use the existing tab if the intent comes back to us
220 if (tab != null) {
221 if (tab.getAppId() == null) {
Michael Kolbf1286a42012-04-17 14:10:52 -0700222 tab.setAppId(mActivity.getPackageName() + "-" + tab.getId());
John Reck8bcafc12011-08-29 16:43:02 -0700223 }
224 intent.putExtra(Browser.EXTRA_APPLICATION_ID, tab.getAppId());
225 }
John Reckdb3d43d2011-02-11 11:56:38 -0800226 // Make sure webkit can handle it internally before checking for specialized
227 // handlers. If webkit can't handle it internally, we need to call
228 // startActivityIfNeeded
229 Matcher m = UrlUtils.ACCEPTED_URI_SCHEMA.matcher(url);
230 if (m.matches() && !isSpecializedHandlerAvailable(intent)) {
John Reck95a49ff2011-02-08 18:23:22 -0800231 return false;
232 }
Russell Brennerd4afde12011-01-07 11:09:36 -0800233 try {
John Reck38b39652012-06-05 09:22:59 -0700234 intent.putExtra(BrowserActivity.EXTRA_DISABLE_URL_OVERRIDE, true);
Russell Brennerd4afde12011-01-07 11:09:36 -0800235 if (mActivity.startActivityIfNeeded(intent, -1)) {
236 // before leaving BrowserActivity, close the empty child tab.
237 // If a new tab is created through JavaScript open to load this
238 // url, we would like to close it as we will load this url in a
239 // different Activity.
John Reck8bcafc12011-08-29 16:43:02 -0700240 mController.closeEmptyTab();
Russell Brennerd4afde12011-01-07 11:09:36 -0800241 return true;
242 }
243 } catch (ActivityNotFoundException ex) {
244 // ignore the error. If no application can handle the URL,
245 // eg about:blank, assume the browser can handle it.
246 }
247
248 return false;
249 }
250
John Reck95a49ff2011-02-08 18:23:22 -0800251 /**
252 * Search for intent handlers that are specific to this URL
253 * aka, specialized apps like google maps or youtube
254 */
255 private boolean isSpecializedHandlerAvailable(Intent intent) {
256 PackageManager pm = mActivity.getPackageManager();
257 List<ResolveInfo> handlers = pm.queryIntentActivities(intent,
258 PackageManager.GET_RESOLVED_FILTER);
259 if (handlers == null || handlers.size() == 0) {
260 return false;
261 }
262 for (ResolveInfo resolveInfo : handlers) {
263 IntentFilter filter = resolveInfo.filter;
264 if (filter == null) {
265 // No intent filter matches this intent?
266 // Error on the side of staying in the browser, ignore
267 continue;
268 }
John Reck1e6a2872011-12-15 14:35:02 -0800269 if (filter.countDataAuthorities() == 0 && filter.countDataPaths() == 0) {
John Reck95a49ff2011-02-08 18:23:22 -0800270 // Generic handler, skip
271 continue;
272 }
273 return true;
274 }
275 return false;
276 }
277
Russell Brenner14e1ae22011-01-12 14:54:23 -0800278 // In case a physical keyboard is attached, handle clicks with the menu key
279 // depressed by opening in a new tab
Russell Brennerca9898e2011-01-21 13:34:02 -0800280 boolean handleMenuClick(Tab tab, String url) {
Russell Brenner14e1ae22011-01-12 14:54:23 -0800281 if (mController.isMenuDown()) {
Michael Kolb7bcafde2011-05-09 13:55:59 -0700282 mController.openTab(url,
283 (tab != null) && tab.isPrivateBrowsingEnabled(),
284 !BrowserSettings.getInstance().openInBackground(), true);
Russell Brenner14e1ae22011-01-12 14:54:23 -0800285 mActivity.closeOptionsMenu();
286 return true;
287 }
288
289 return false;
290 }
291
Michael Kolb8233fac2010-10-26 16:08:53 -0700292}