blob: 72704e01228f7e1276ffb9847abc36c118fe39b1 [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
17package com.android.browser;
18
19import android.app.Activity;
20import android.content.ActivityNotFoundException;
21import android.content.Intent;
22import android.content.pm.PackageManager;
23import android.database.Cursor;
24import android.net.Uri;
25import android.util.Log;
26import android.webkit.WebView;
27
28import java.net.URISyntaxException;
29
30/**
31 *
32 */
33public class UrlHandler {
34
35 // Use in overrideUrlLoading
36 /* package */ final static String SCHEME_WTAI = "wtai://wp/";
37 /* package */ final static String SCHEME_WTAI_MC = "wtai://wp/mc;";
38 /* package */ final static String SCHEME_WTAI_SD = "wtai://wp/sd;";
39 /* package */ final static String SCHEME_WTAI_AP = "wtai://wp/ap;";
40
41 Controller mController;
42 Activity mActivity;
43
44 private Boolean mIsProviderPresent = null;
45 private Uri mRlzUri = null;
46
47 public UrlHandler(Controller controller) {
48 mController = controller;
49 mActivity = mController.getActivity();
50 }
51
52 boolean shouldOverrideUrlLoading(WebView view, String url) {
53 if (view.isPrivateBrowsingEnabled()) {
54 // Don't allow urls to leave the browser app when in
55 // private browsing mode
56 mController.loadUrl(view, url);
57 return true;
58 }
59
60 if (url.startsWith(SCHEME_WTAI)) {
61 // wtai://wp/mc;number
62 // number=string(phone-number)
63 if (url.startsWith(SCHEME_WTAI_MC)) {
64 Intent intent = new Intent(Intent.ACTION_VIEW,
65 Uri.parse(WebView.SCHEME_TEL +
66 url.substring(SCHEME_WTAI_MC.length())));
67 mActivity.startActivity(intent);
68 // before leaving BrowserActivity, close the empty child tab.
69 // If a new tab is created through JavaScript open to load this
70 // url, we would like to close it as we will load this url in a
71 // different Activity.
72 mController.closeEmptyChildTab();
73 return true;
74 }
75 // wtai://wp/sd;dtmf
76 // dtmf=string(dialstring)
77 if (url.startsWith(SCHEME_WTAI_SD)) {
78 // TODO: only send when there is active voice connection
79 return false;
80 }
81 // wtai://wp/ap;number;name
82 // number=string(phone-number)
83 // name=string
84 if (url.startsWith(SCHEME_WTAI_AP)) {
85 // TODO
86 return false;
87 }
88 }
89
90 // The "about:" schemes are internal to the browser; don't want these to
91 // be dispatched to other apps.
92 if (url.startsWith("about:")) {
93 return false;
94 }
95
96 // If this is a Google search, attempt to add an RLZ string
97 // (if one isn't already present).
98 if (rlzProviderPresent()) {
99 Uri siteUri = Uri.parse(url);
100 if (needsRlzString(siteUri)) {
101 String rlz = null;
102 Cursor cur = null;
103 try {
104 cur = mActivity.getContentResolver()
105 .query(getRlzUri(), null, null, null, null);
106 if (cur != null && cur.moveToFirst() && !cur.isNull(0)) {
107 url = siteUri.buildUpon()
108 .appendQueryParameter("rlz", cur.getString(0))
109 .build().toString();
110 }
111 } finally {
112 if (cur != null) {
113 cur.close();
114 }
115 }
116 mController.loadUrl(view, url);
117 return true;
118 }
119 }
120
121 Intent intent;
122 // perform generic parsing of the URI to turn it into an Intent.
123 try {
124 intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
125 } catch (URISyntaxException ex) {
126 Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
127 return false;
128 }
129
130 // check whether the intent can be resolved. If not, we will see
131 // whether we can download it from the Market.
132 if (mActivity.getPackageManager().resolveActivity(intent, 0) == null) {
133 String packagename = intent.getPackage();
134 if (packagename != null) {
135 intent = new Intent(Intent.ACTION_VIEW, Uri
136 .parse("market://search?q=pname:" + packagename));
137 intent.addCategory(Intent.CATEGORY_BROWSABLE);
138 mActivity.startActivity(intent);
139 // before leaving BrowserActivity, close the empty child tab.
140 // If a new tab is created through JavaScript open to load this
141 // url, we would like to close it as we will load this url in a
142 // different Activity.
143 mController.closeEmptyChildTab();
144 return true;
145 } else {
146 return false;
147 }
148 }
149
150 // sanitize the Intent, ensuring web pages can not bypass browser
151 // security (only access to BROWSABLE activities).
152 intent.addCategory(Intent.CATEGORY_BROWSABLE);
153 intent.setComponent(null);
154 try {
155 if (mActivity.startActivityIfNeeded(intent, -1)) {
156 // before leaving BrowserActivity, close the empty child tab.
157 // If a new tab is created through JavaScript open to load this
158 // url, we would like to close it as we will load this url in a
159 // different Activity.
160 mController.closeEmptyChildTab();
161 return true;
162 }
163 } catch (ActivityNotFoundException ex) {
164 // ignore the error. If no application can handle the URL,
165 // eg about:blank, assume the browser can handle it.
166 }
167
168 if (mController.isMenuDown()) {
169 mController.openTab(url, false);
170 mActivity.closeOptionsMenu();
171 return true;
172 }
173 return false;
174 }
175
176 // Determine whether the RLZ provider is present on the system.
177 private boolean rlzProviderPresent() {
178 if (mIsProviderPresent == null) {
179 PackageManager pm = mActivity.getPackageManager();
180 mIsProviderPresent = pm.resolveContentProvider(
181 BrowserSettings.RLZ_PROVIDER, 0) != null;
182 }
183 return mIsProviderPresent;
184 }
185
186 // Retrieve the RLZ access point string and cache the URI used to
187 // retrieve RLZ values.
188 private Uri getRlzUri() {
189 if (mRlzUri == null) {
190 String ap = mActivity.getResources()
191 .getString(R.string.rlz_access_point);
192 mRlzUri = Uri.withAppendedPath(BrowserSettings.RLZ_PROVIDER_URI, ap);
193 }
194 return mRlzUri;
195 }
196
197 // Determine if this URI appears to be for a Google search
198 // and does not have an RLZ parameter.
199 // Taken largely from Chrome source, src/chrome/browser/google_url_tracker.cc
200 private static boolean needsRlzString(Uri uri) {
201 String scheme = uri.getScheme();
202 if (("http".equals(scheme) || "https".equals(scheme)) &&
203 (uri.getQueryParameter("q") != null) &&
204 (uri.getQueryParameter("rlz") == null)) {
205 String host = uri.getHost();
206 if (host == null) {
207 return false;
208 }
209 String[] hostComponents = host.split("\\.");
210
211 if (hostComponents.length < 2) {
212 return false;
213 }
214 int googleComponent = hostComponents.length - 2;
215 String component = hostComponents[googleComponent];
216 if (!"google".equals(component)) {
217 if (hostComponents.length < 3 ||
218 (!"co".equals(component) && !"com".equals(component))) {
219 return false;
220 }
221 googleComponent = hostComponents.length - 3;
222 if (!"google".equals(hostComponents[googleComponent])) {
223 return false;
224 }
225 }
226
227 // Google corp network handling.
228 if (googleComponent > 0 && "corp".equals(
229 hostComponents[googleComponent - 1])) {
230 return false;
231 }
232
233 return true;
234 }
235 return false;
236 }
237
238}