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