/WebVox/src/com/marvin/webvox/BrowserActivity.java
Java | 1668 lines | 1094 code | 158 blank | 416 comment | 277 complexity | 7061e778ccf953fd7c2ebe0b8df697e4 MD5 | raw file
Large files files are truncated, but you can click here to view the full file
1/* 2 * Copyright (C) 2006 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.marvin.webvox; 18 19import com.marvin.webvox.R; 20//import com.google.android.googleapps.IGoogleLoginService; 21//import com.google.android.googlelogin.GoogleLoginServiceConstants; 22 23import android.app.Activity; 24import android.app.AlertDialog; 25import android.app.ProgressDialog; 26import android.app.SearchManager; 27import android.content.ActivityNotFoundException; 28import android.content.BroadcastReceiver; 29import android.content.ComponentName; 30import android.content.ContentResolver; 31import android.content.ContentUris; 32import android.content.ContentValues; 33import android.content.Context; 34import android.content.DialogInterface; 35import android.content.Intent; 36import android.content.IntentFilter; 37import android.content.ServiceConnection; 38import android.content.DialogInterface.OnCancelListener; 39import android.content.pm.PackageInfo; 40import android.content.pm.PackageManager; 41import android.content.pm.ResolveInfo; 42import android.content.res.AssetManager; 43import android.content.res.Configuration; 44import android.content.res.Resources; 45import android.database.Cursor; 46import android.database.sqlite.SQLiteDatabase; 47import android.database.sqlite.SQLiteException; 48import android.graphics.Bitmap; 49import android.graphics.BitmapFactory; 50import android.graphics.Canvas; 51import android.graphics.DrawFilter; 52import android.graphics.Paint; 53import android.graphics.PaintFlagsDrawFilter; 54import android.graphics.Picture; 55import android.graphics.PixelFormat; 56import android.graphics.Rect; 57import android.graphics.drawable.Drawable; 58import android.hardware.SensorListener; 59import android.hardware.SensorManager; 60import android.net.ConnectivityManager; 61import android.net.NetworkInfo; 62import android.net.Uri; 63//import android.net.WebAddress; 64//import android.net.http.EventHandler; 65import android.net.http.SslCertificate; 66//import android.net.http.SslError; 67import android.os.AsyncTask; 68import android.os.Bundle; 69import android.os.Debug; 70import android.os.Environment; 71import android.os.Handler; 72import android.os.IBinder; 73import android.os.Message; 74import android.os.PowerManager; 75import android.os.Process; 76import android.os.RemoteException; 77//import android.os.ServiceManager; 78import android.os.SystemClock; 79import android.provider.Browser; 80import android.provider.ContactsContract; 81import android.provider.ContactsContract.Intents.Insert; 82//import android.provider.Downloads; 83import android.provider.MediaStore; 84//import android.text.IClipboard; 85import android.speech.tts.TextToSpeech; 86import android.text.TextUtils; 87import android.text.format.DateFormat; 88//import android.text.util.Regex; 89import android.util.AttributeSet; 90import android.util.Log; 91import android.view.ContextMenu; 92import android.view.Gravity; 93import android.view.KeyEvent; 94import android.view.LayoutInflater; 95import android.view.Menu; 96import android.view.MenuInflater; 97import android.view.MenuItem; 98import android.view.View; 99import android.view.ViewGroup; 100import android.view.Window; 101import android.view.WindowManager; 102import android.view.ContextMenu.ContextMenuInfo; 103import android.view.MenuItem.OnMenuItemClickListener; 104import android.view.animation.AlphaAnimation; 105import android.view.animation.Animation; 106import android.view.animation.AnimationSet; 107import android.view.animation.DecelerateInterpolator; 108import android.view.animation.ScaleAnimation; 109import android.view.animation.TranslateAnimation; 110import android.webkit.CookieManager; 111import android.webkit.CookieSyncManager; 112import android.webkit.DownloadListener; 113import android.webkit.GeolocationPermissions; 114import android.webkit.HttpAuthHandler; 115//import android.webkit.PluginManager; 116import android.webkit.SslErrorHandler; 117import android.webkit.URLUtil; 118//import android.webkit.ValueCallback; 119import android.webkit.WebChromeClient; 120//import android.webkit.WebChromeClient.CustomViewCallback; 121import android.webkit.WebHistoryItem; 122import android.webkit.WebIconDatabase; 123import android.webkit.WebStorage; 124import android.webkit.WebView; 125import android.webkit.WebViewClient; 126import android.widget.EditText; 127import android.widget.FrameLayout; 128import android.widget.LinearLayout; 129import android.widget.TextView; 130import android.widget.Toast; 131 132import java.io.BufferedOutputStream; 133import java.io.ByteArrayOutputStream; 134import java.io.File; 135import java.io.FileInputStream; 136import java.io.FileOutputStream; 137import java.io.IOException; 138import java.io.InputStream; 139import java.net.MalformedURLException; 140import java.net.URI; 141import java.net.URISyntaxException; 142import java.net.URL; 143import java.net.URLEncoder; 144import java.text.ParseException; 145import java.util.Date; 146import java.util.Enumeration; 147import java.util.HashMap; 148import java.util.LinkedList; 149import java.util.List; 150import java.util.Vector; 151import java.util.regex.Matcher; 152import java.util.regex.Pattern; 153import java.util.zip.ZipEntry; 154import java.util.zip.ZipFile; 155 156public class BrowserActivity extends Activity 157 implements View.OnCreateContextMenuListener, 158 DownloadListener { 159 160 /* Define some aliases to make these debugging flags easier to refer to. 161 * This file imports android.provider.Browser, so we can't just refer to "Browser.DEBUG". 162 */ 163 private final static boolean DEBUG = com.marvin.webvox.Browser.DEBUG; 164 private final static boolean LOGV_ENABLED = com.marvin.webvox.Browser.LOGV_ENABLED; 165 private final static boolean LOGD_ENABLED = com.marvin.webvox.Browser.LOGD_ENABLED; 166 167// private IGoogleLoginService mGls = null; 168 private ServiceConnection mGlsConnection = null; 169 170 private SensorManager mSensorManager = null; 171 172 // These are single-character shortcuts for searching popular sources. 173 private static final int SHORTCUT_INVALID = 0; 174 private static final int SHORTCUT_GOOGLE_SEARCH = 1; 175 private static final int SHORTCUT_WIKIPEDIA_SEARCH = 2; 176 private static final int SHORTCUT_DICTIONARY_SEARCH = 3; 177 private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4; 178 179 /* Whitelisted webpages 180 private static HashSet<String> sWhiteList; 181 182 static { 183 sWhiteList = new HashSet<String>(); 184 sWhiteList.add("cnn.com/"); 185 sWhiteList.add("espn.go.com/"); 186 sWhiteList.add("nytimes.com/"); 187 sWhiteList.add("engadget.com/"); 188 sWhiteList.add("yahoo.com/"); 189 sWhiteList.add("msn.com/"); 190 sWhiteList.add("amazon.com/"); 191 sWhiteList.add("consumerist.com/"); 192 sWhiteList.add("google.com/m/news"); 193 } 194 */ 195 196 private void setupHomePage() { 197 final Runnable getAccount = new Runnable() { 198 public void run() { 199 // Lower priority 200 Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); 201 // get the default home page 202 String homepage = mSettings.getHomePage(); 203 204 try { 205// if (mGls == null) return; 206 207 if (!homepage.startsWith("http://www.google.")) return; 208 if (homepage.indexOf('?') == -1) return; 209 210// String hostedUser = mGls.getAccount(GoogleLoginServiceConstants.PREFER_HOSTED); 211// String googleUser = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE); 212 213 // three cases: 214 // 215 // hostedUser == googleUser 216 // The device has only a google account 217 // 218 // hostedUser != googleUser 219 // The device has a hosted account and a google account 220 // 221 // hostedUser != null, googleUser == null 222 // The device has only a hosted account (so far) 223 224 // developers might have no accounts at all 225// if (hostedUser == null) return; 226 227// if (googleUser == null || !hostedUser.equals(googleUser)) { 228// String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1); 229// homepage = homepage.replace("?", "/a/" + domain + "?"); 230// } 231// } catch (RemoteException ignore) { 232 // Login service died; carry on 233 } catch (RuntimeException ignore) { 234 // Login service died; carry on 235 } finally { 236 finish(homepage); 237 } 238 } 239 240 private void finish(final String homepage) { 241 mHandler.post(new Runnable() { 242 public void run() { 243 mSettings.setHomePage(BrowserActivity.this, homepage); 244 resumeAfterCredentials(); 245 246 // as this is running in a separate thread, 247 // BrowserActivity's onDestroy() may have been called, 248 // which also calls unbindService(). 249 if (mGlsConnection != null) { 250 // we no longer need to keep GLS open 251 unbindService(mGlsConnection); 252 mGlsConnection = null; 253 } 254 } }); 255 } }; 256 257 final boolean[] done = { false }; 258 259 // Open a connection to the Google Login Service. The first 260 // time the connection is established, set up the homepage depending on 261 // the account in a background thread. 262 mGlsConnection = new ServiceConnection() { 263 public void onServiceConnected(ComponentName className, IBinder service) { 264// mGls = IGoogleLoginService.Stub.asInterface(service); 265 if (done[0] == false) { 266 done[0] = true; 267 Thread account = new Thread(getAccount); 268 account.setName("GLSAccount"); 269 account.start(); 270 } 271 } 272 public void onServiceDisconnected(ComponentName className) { 273// mGls = null; 274 } 275 }; 276 277// bindService(GoogleLoginServiceConstants.SERVICE_INTENT, 278// mGlsConnection, Context.BIND_AUTO_CREATE); 279 } 280 281 private static class ClearThumbnails extends AsyncTask<File, Void, Void> { 282 @Override 283 public Void doInBackground(File... files) { 284 if (files != null) { 285 for (File f : files) { 286 if (!f.delete()) { 287 Log.e(LOGTAG, f.getPath() + " was not deleted"); 288 } 289 } 290 } 291 return null; 292 } 293 } 294 295 /** 296 * This layout holds everything you see below the status bar, including the 297 * error console, the custom view container, and the webviews. 298 */ 299 private FrameLayout mBrowserFrameLayout; 300 301 @Override public void onCreate(Bundle icicle) { 302 mTts = new TextToSpeech(this, null); 303 scriptdb = new ScriptDatabase(this); 304 scriptdb.onUpgrade(scriptdb.getWritableDatabase(), -10, 10); 305 306 if (LOGV_ENABLED) { 307 Log.v(LOGTAG, this + " onStart"); 308 } 309 super.onCreate(icicle); 310 // test the browser in OpenGL 311 // requestWindowFeature(Window.FEATURE_OPENGL); 312 313 setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL); 314 315 mResolver = getContentResolver(); 316 317 // If this was a web search request, pass it on to the default web 318 // search provider and finish this activity. 319 if (handleWebSearchIntent(getIntent())) { 320 finish(); 321 return; 322 } 323 324 // 325 // start MASF proxy service 326 // 327 //Intent proxyServiceIntent = new Intent(); 328 //proxyServiceIntent.setComponent 329 // (new ComponentName( 330 // "com.android.masfproxyservice", 331 // "com.android.masfproxyservice.MasfProxyService")); 332 //startService(proxyServiceIntent, null); 333 334 mSecLockIcon = Resources.getSystem().getDrawable( 335 android.R.drawable.ic_secure); 336 mMixLockIcon = Resources.getSystem().getDrawable( 337 android.R.drawable.ic_partial_secure); 338 339// FrameLayout frameLayout = (FrameLayout) getWindow().getDecorView() 340// .findViewById(com.android.internal.R.id.content); 341 342 343 mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(this) 344 .inflate(R.layout.custom_screen, null); 345 mContentView = (FrameLayout) mBrowserFrameLayout.findViewById( 346 R.id.main_content); 347 mErrorConsoleContainer = (LinearLayout) mBrowserFrameLayout 348 .findViewById(R.id.error_console); 349 mCustomViewContainer = (FrameLayout) mBrowserFrameLayout 350 .findViewById(R.id.fullscreen_custom_content); 351 //frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS); 352 353 setContentView(mBrowserFrameLayout); 354 355 mTitleBar = new TitleBar(this); 356 357 // Create the tab control and our initial tab 358 mTabControl = new TabControl(this); 359 360 // Open the icon database and retain all the bookmark urls for favicons 361 retainIconsOnStartup(); 362 363 // Keep a settings instance handy. 364 mSettings = BrowserSettings.getInstance(); 365 mSettings.setTabControl(mTabControl); 366 mSettings.loadFromDb(this); 367 368 PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE); 369 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser"); 370 371 /* enables registration for changes in network status from 372 http stack */ 373 mNetworkStateChangedFilter = new IntentFilter(); 374 mNetworkStateChangedFilter.addAction( 375 ConnectivityManager.CONNECTIVITY_ACTION); 376 mNetworkStateIntentReceiver = new BroadcastReceiver() { 377 @Override 378 public void onReceive(Context context, Intent intent) { 379 if (intent.getAction().equals( 380 ConnectivityManager.CONNECTIVITY_ACTION)) { 381 boolean noConnectivity = intent.getBooleanExtra( 382 ConnectivityManager.EXTRA_NO_CONNECTIVITY, false); 383 onNetworkToggle(!noConnectivity); 384 } 385 } 386 }; 387 388 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); 389 filter.addAction(Intent.ACTION_PACKAGE_REMOVED); 390 filter.addDataScheme("package"); 391 mPackageInstallationReceiver = new BroadcastReceiver() { 392 @Override 393 public void onReceive(Context context, Intent intent) { 394 final String action = intent.getAction(); 395 final String packageName = intent.getData() 396 .getSchemeSpecificPart(); 397 final boolean replacing = intent.getBooleanExtra( 398 Intent.EXTRA_REPLACING, false); 399 if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) { 400 // if it is replacing, refreshPlugins() when adding 401 return; 402 } 403 PackageManager pm = BrowserActivity.this.getPackageManager(); 404 PackageInfo pkgInfo = null; 405 try { 406 pkgInfo = pm.getPackageInfo(packageName, 407 PackageManager.GET_PERMISSIONS); 408 } catch (PackageManager.NameNotFoundException e) { 409 return; 410 } 411 if (pkgInfo != null) { 412 String permissions[] = pkgInfo.requestedPermissions; 413 if (permissions == null) { 414 return; 415 } 416 boolean permissionOk = false; 417 /* 418 for (String permit : permissions) { 419 if (PluginManager.PLUGIN_PERMISSION.equals(permit)) { 420 permissionOk = true; 421 break; 422 } 423 } 424 if (permissionOk) { 425 PluginManager.getInstance(BrowserActivity.this) 426 .refreshPlugins( 427 Intent.ACTION_PACKAGE_ADDED 428 .equals(action)); 429 } 430 */ 431 } 432 } 433 }; 434 registerReceiver(mPackageInstallationReceiver, filter); 435 436 if (!mTabControl.restoreState(icicle)) { 437 // clear up the thumbnail directory if we can't restore the state as 438 // none of the files in the directory are referenced any more. 439 new ClearThumbnails().execute( 440 mTabControl.getThumbnailDir().listFiles()); 441 // there is no quit on Android. But if we can't restore the state, 442 // we can treat it as a new Browser, remove the old session cookies. 443 CookieManager.getInstance().removeSessionCookie(); 444 final Intent intent = getIntent(); 445 final Bundle extra = intent.getExtras(); 446 // Create an initial tab. 447 // If the intent is ACTION_VIEW and data is not null, the Browser is 448 // invoked to view the content by another application. In this case, 449 // the tab will be close when exit. 450 UrlData urlData = getUrlDataFromIntent(intent); 451 452 final TabControl.Tab t = mTabControl.createNewTab( 453 Intent.ACTION_VIEW.equals(intent.getAction()) && 454 intent.getData() != null, 455 intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), urlData.mUrl); 456 mTabControl.setCurrentTab(t); 457 attachTabToContentView(t); 458 WebView webView = t.getWebView(); 459 if (extra != null) { 460 int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0); 461 if (scale > 0 && scale <= 1000) { 462 webView.setInitialScale(scale); 463 } 464 } 465 // If we are not restoring from an icicle, then there is a high 466 // likely hood this is the first run. So, check to see if the 467 // homepage needs to be configured and copy any plugins from our 468 // asset directory to the data partition. 469 if ((extra == null || !extra.getBoolean("testing")) 470 && !mSettings.isLoginInitialized()) { 471 setupHomePage(); 472 } 473 474 if (urlData.isEmpty()) { 475 if (mSettings.isLoginInitialized()) { 476 webView.loadUrl(mSettings.getHomePage()); 477 } else { 478 waitForCredentials(); 479 } 480 } else { 481 if (extra != null) { 482// urlData.setPostData(extra 483// .getByteArray(Browser.EXTRA_POST_DATA)); 484 } 485 urlData.loadIn(webView); 486 } 487 } else { 488 // TabControl.restoreState() will create a new tab even if 489 // restoring the state fails. 490 attachTabToContentView(mTabControl.getCurrentTab()); 491 } 492 493 // Read JavaScript flags if it exists. 494 String jsFlags = mSettings.getJsFlags(); 495 if (jsFlags.trim().length() != 0) { 496// mTabControl.getCurrentWebView().setJsFlags(jsFlags); 497 } 498 } 499 500 @Override 501 protected void onNewIntent(Intent intent) { 502 TabControl.Tab current = mTabControl.getCurrentTab(); 503 // When a tab is closed on exit, the current tab index is set to -1. 504 // Reset before proceed as Browser requires the current tab to be set. 505 if (current == null) { 506 // Try to reset the tab in case the index was incorrect. 507 current = mTabControl.getTab(0); 508 if (current == null) { 509 // No tabs at all so just ignore this intent. 510 return; 511 } 512 mTabControl.setCurrentTab(current); 513 attachTabToContentView(current); 514 resetTitleAndIcon(current.getWebView()); 515 } 516 final String action = intent.getAction(); 517 final int flags = intent.getFlags(); 518 if (Intent.ACTION_MAIN.equals(action) || 519 (flags & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) != 0) { 520 // just resume the browser 521 return; 522 } 523 if (Intent.ACTION_VIEW.equals(action) 524 || Intent.ACTION_SEARCH.equals(action) 525 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) 526 || Intent.ACTION_WEB_SEARCH.equals(action)) { 527 // If this was a search request (e.g. search query directly typed into the address bar), 528 // pass it on to the default web search provider. 529 if (handleWebSearchIntent(intent)) { 530 return; 531 } 532 533 UrlData urlData = getUrlDataFromIntent(intent); 534 if (urlData.isEmpty()) { 535 urlData = new UrlData(mSettings.getHomePage()); 536 } 537// urlData.setPostData(intent 538// .getByteArrayExtra(Browser.EXTRA_POST_DATA)); 539 540 final String appId = intent 541 .getStringExtra(Browser.EXTRA_APPLICATION_ID); 542 if (Intent.ACTION_VIEW.equals(action) 543 && !getPackageName().equals(appId) 544 && (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) { 545 TabControl.Tab appTab = mTabControl.getTabFromId(appId); 546 if (appTab != null) { 547 Log.i(LOGTAG, "Reusing tab for " + appId); 548 // Dismiss the subwindow if applicable. 549 dismissSubWindow(appTab); 550 // Since we might kill the WebView, remove it from the 551 // content view first. 552 removeTabFromContentView(appTab); 553 // Recreate the main WebView after destroying the old one. 554 // If the WebView has the same original url and is on that 555 // page, it can be reused. 556 boolean needsLoad = 557 mTabControl.recreateWebView(appTab, urlData.mUrl); 558 559 if (current != appTab) { 560 switchToTab(mTabControl.getTabIndex(appTab)); 561 if (needsLoad) { 562 urlData.loadIn(appTab.getWebView()); 563 } 564 } else { 565 // If the tab was the current tab, we have to attach 566 // it to the view system again. 567 attachTabToContentView(appTab); 568 if (needsLoad) { 569 urlData.loadIn(appTab.getWebView()); 570 } 571 } 572 return; 573 } else { 574 // No matching application tab, try to find a regular tab 575 // with a matching url. 576 appTab = mTabControl.findUnusedTabWithUrl(urlData.mUrl); 577 if (appTab != null) { 578 if (current != appTab) { 579 switchToTab(mTabControl.getTabIndex(appTab)); 580 } 581 // Otherwise, we are already viewing the correct tab. 582 } else { 583 // if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url 584 // will be opened in a new tab unless we have reached 585 // MAX_TABS. Then the url will be opened in the current 586 // tab. If a new tab is created, it will have "true" for 587 // exit on close. 588 openTabAndShow(urlData, true, appId); 589 } 590 } 591 } else { 592 if ("about:debug".equals(urlData.mUrl)) { 593 mSettings.toggleDebugSettings(); 594 return; 595 } 596 // Get rid of the subwindow if it exists 597 dismissSubWindow(current); 598 urlData.loadIn(current.getWebView()); 599 } 600 } 601 } 602 603 private int parseUrlShortcut(String url) { 604 if (url == null) return SHORTCUT_INVALID; 605 606 // FIXME: quick search, need to be customized by setting 607 if (url.length() > 2 && url.charAt(1) == ' ') { 608 switch (url.charAt(0)) { 609 case 'g': return SHORTCUT_GOOGLE_SEARCH; 610 case 'w': return SHORTCUT_WIKIPEDIA_SEARCH; 611 case 'd': return SHORTCUT_DICTIONARY_SEARCH; 612 case 'l': return SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH; 613 } 614 } 615 return SHORTCUT_INVALID; 616 } 617 618 /** 619 * Launches the default web search activity with the query parameters if the given intent's data 620 * are identified as plain search terms and not URLs/shortcuts. 621 * @return true if the intent was handled and web search activity was launched, false if not. 622 */ 623 private boolean handleWebSearchIntent(Intent intent) { 624 if (intent == null) return false; 625 626 String url = null; 627 final String action = intent.getAction(); 628 if (Intent.ACTION_VIEW.equals(action)) { 629 Uri data = intent.getData(); 630 if (data != null) url = data.toString(); 631 } else if (Intent.ACTION_SEARCH.equals(action) 632 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) 633 || Intent.ACTION_WEB_SEARCH.equals(action)) { 634 url = intent.getStringExtra(SearchManager.QUERY); 635 } 636 return handleWebSearchRequest(url, intent.getBundleExtra(SearchManager.APP_DATA), 637 intent.getStringExtra(SearchManager.EXTRA_DATA_KEY)); 638 } 639 640 /** 641 * Launches the default web search activity with the query parameters if the given url string 642 * was identified as plain search terms and not URL/shortcut. 643 * @return true if the request was handled and web search activity was launched, false if not. 644 */ 645 private boolean handleWebSearchRequest(String inUrl, Bundle appData, String extraData) { 646 if (inUrl == null) return false; 647 648 // In general, we shouldn't modify URL from Intent. 649 // But currently, we get the user-typed URL from search box as well. 650 String url = fixUrl(inUrl).trim(); 651 652 // URLs and site specific search shortcuts are handled by the regular flow of control, so 653 // return early. 654// if (Regex.WEB_URL_PATTERN.matcher(url).matches() || 655if ( ACCEPTED_URI_SCHEMA.matcher(url).matches() 656 || parseUrlShortcut(url) != SHORTCUT_INVALID) { 657 return false; 658 } 659 660 Browser.updateVisitedHistory(mResolver, url, false); 661 Browser.addSearchUrl(mResolver, url); 662 663 Intent intent = new Intent(Intent.ACTION_WEB_SEARCH); 664 intent.addCategory(Intent.CATEGORY_DEFAULT); 665 intent.putExtra(SearchManager.QUERY, url); 666 if (appData != null) { 667 intent.putExtra(SearchManager.APP_DATA, appData); 668 } 669 if (extraData != null) { 670 intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData); 671 } 672 intent.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName()); 673 startActivity(intent); 674 675 return true; 676 } 677 678 private UrlData getUrlDataFromIntent(Intent intent) { 679 String url = null; 680 if (intent != null) { 681 final String action = intent.getAction(); 682 if (Intent.ACTION_VIEW.equals(action)) { 683 url = smartUrlFilter(intent.getData()); 684 if (url != null && url.startsWith("content:")) { 685 /* Append mimetype so webview knows how to display */ 686 String mimeType = intent.resolveType(getContentResolver()); 687 if (mimeType != null) { 688 url += "?" + mimeType; 689 } 690 } 691// if ("inline:".equals(url)) { 692// return new InlinedUrlData( 693// intent.getStringExtra(Browser.EXTRA_INLINE_CONTENT), 694// intent.getType(), 695// intent.getStringExtra(Browser.EXTRA_INLINE_ENCODING), 696// intent.getStringExtra(Browser.EXTRA_INLINE_FAILURL)); 697// } 698 } else if (Intent.ACTION_SEARCH.equals(action) 699 || MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action) 700 || Intent.ACTION_WEB_SEARCH.equals(action)) { 701 url = intent.getStringExtra(SearchManager.QUERY); 702 if (url != null) { 703 mLastEnteredUrl = url; 704 // Don't add Urls, just search terms. 705 // Urls will get added when the page is loaded. 706// if (!Regex.WEB_URL_PATTERN.matcher(url).matches()) { 707// Browser.updateVisitedHistory(mResolver, url, false); 708// } 709 // In general, we shouldn't modify URL from Intent. 710 // But currently, we get the user-typed URL from search box as well. 711 url = fixUrl(url); 712 url = smartUrlFilter(url); 713 String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&"; 714 if (url.contains(searchSource)) { 715 String source = null; 716 final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA); 717// if (appData != null) { 718// source = appData.getString(SearchManager.SOURCE); 719// } 720 if (TextUtils.isEmpty(source)) { 721 source = GOOGLE_SEARCH_SOURCE_UNKNOWN; 722 } 723 url = url.replace(searchSource, "&source=android-"+source+"&"); 724 } 725 } 726 } 727 } 728 return new UrlData(url); 729 } 730 731 /* package */ static String fixUrl(String inUrl) { 732 // FIXME: Converting the url to lower case 733 // duplicates functionality in smartUrlFilter(). 734 // However, changing all current callers of fixUrl to 735 // call smartUrlFilter in addition may have unwanted 736 // consequences, and is deferred for now. 737 int colon = inUrl.indexOf(':'); 738 boolean allLower = true; 739 for (int index = 0; index < colon; index++) { 740 char ch = inUrl.charAt(index); 741 if (!Character.isLetter(ch)) { 742 break; 743 } 744 allLower &= Character.isLowerCase(ch); 745 if (index == colon - 1 && !allLower) { 746 inUrl = inUrl.substring(0, colon).toLowerCase() 747 + inUrl.substring(colon); 748 } 749 } 750 if (inUrl.startsWith("http://") || inUrl.startsWith("https://")) 751 return inUrl; 752 if (inUrl.startsWith("http:") || 753 inUrl.startsWith("https:")) { 754 if (inUrl.startsWith("http:/") || inUrl.startsWith("https:/")) { 755 inUrl = inUrl.replaceFirst("/", "//"); 756 } else inUrl = inUrl.replaceFirst(":", "://"); 757 } 758 return inUrl; 759 } 760 761 /** 762 * Looking for the pattern like this 763 * 764 * * 765 * * * 766 * *** * ******* 767 * * * 768 * * * 769 * * 770 */ 771 private final SensorListener mSensorListener = new SensorListener() { 772 private long mLastGestureTime; 773 private float[] mPrev = new float[3]; 774 private float[] mPrevDiff = new float[3]; 775 private float[] mDiff = new float[3]; 776 private float[] mRevertDiff = new float[3]; 777 778 public void onSensorChanged(int sensor, float[] values) { 779 boolean show = false; 780 float[] diff = new float[3]; 781 782 for (int i = 0; i < 3; i++) { 783 diff[i] = values[i] - mPrev[i]; 784 if (Math.abs(diff[i]) > 1) { 785 show = true; 786 } 787 if ((diff[i] > 1.0 && mDiff[i] < 0.2) 788 || (diff[i] < -1.0 && mDiff[i] > -0.2)) { 789 // start track when there is a big move, or revert 790 mRevertDiff[i] = mDiff[i]; 791 mDiff[i] = 0; 792 } else if (diff[i] > -0.2 && diff[i] < 0.2) { 793 // reset when it is flat 794 mDiff[i] = mRevertDiff[i] = 0; 795 } 796 mDiff[i] += diff[i]; 797 mPrevDiff[i] = diff[i]; 798 mPrev[i] = values[i]; 799 } 800 801 if (false) { 802 // only shows if we think the delta is big enough, in an attempt 803 // to detect "serious" moves left/right or up/down 804 Log.d("BrowserSensorHack", "sensorChanged " + sensor + " (" 805 + values[0] + ", " + values[1] + ", " + values[2] + ")" 806 + " diff(" + diff[0] + " " + diff[1] + " " + diff[2] 807 + ")"); 808 Log.d("BrowserSensorHack", " mDiff(" + mDiff[0] + " " 809 + mDiff[1] + " " + mDiff[2] + ")" + " mRevertDiff(" 810 + mRevertDiff[0] + " " + mRevertDiff[1] + " " 811 + mRevertDiff[2] + ")"); 812 } 813 814 long now = android.os.SystemClock.uptimeMillis(); 815 if (now - mLastGestureTime > 1000) { 816 mLastGestureTime = 0; 817 818 float y = mDiff[1]; 819 float z = mDiff[2]; 820 float ay = Math.abs(y); 821 float az = Math.abs(z); 822 float ry = mRevertDiff[1]; 823 float rz = mRevertDiff[2]; 824 float ary = Math.abs(ry); 825 float arz = Math.abs(rz); 826 boolean gestY = ay > 2.5f && ary > 1.0f && ay > ary; 827 boolean gestZ = az > 3.5f && arz > 1.0f && az > arz; 828 829 if ((gestY || gestZ) && !(gestY && gestZ)) { 830 WebView view = mTabControl.getCurrentWebView(); 831 832 if (view != null) { 833 if (gestZ) { 834 if (z < 0) { 835 view.zoomOut(); 836 } else { 837 view.zoomIn(); 838 } 839 } else { 840 view.flingScroll(0, Math.round(y * 100)); 841 } 842 } 843 mLastGestureTime = now; 844 } 845 } 846 } 847 848 public void onAccuracyChanged(int sensor, int accuracy) { 849 // TODO Auto-generated method stub 850 851 } 852 }; 853 854 @Override protected void onResume() { 855 super.onResume(); 856 if (LOGV_ENABLED) { 857 Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this); 858 } 859 860 if (!mActivityInPause) { 861 Log.e(LOGTAG, "BrowserActivity is already resumed."); 862 return; 863 } 864 865 mTabControl.resumeCurrentTab(); 866 mActivityInPause = false; 867 resumeWebViewTimers(); 868 869 if (mWakeLock.isHeld()) { 870 mHandler.removeMessages(RELEASE_WAKELOCK); 871 mWakeLock.release(); 872 } 873 874 if (mCredsDlg != null) { 875 if (!mHandler.hasMessages(CANCEL_CREDS_REQUEST)) { 876 // In case credential request never comes back 877 mHandler.sendEmptyMessageDelayed(CANCEL_CREDS_REQUEST, 6000); 878 } 879 } 880 881 registerReceiver(mNetworkStateIntentReceiver, 882 mNetworkStateChangedFilter); 883 WebView.enablePlatformNotifications(); 884 885 if (mSettings.doFlick()) { 886 if (mSensorManager == null) { 887 mSensorManager = (SensorManager) getSystemService( 888 Context.SENSOR_SERVICE); 889 } 890 mSensorManager.registerListener(mSensorListener, 891 SensorManager.SENSOR_ACCELEROMETER, 892 SensorManager.SENSOR_DELAY_FASTEST); 893 } else { 894 mSensorManager = null; 895 } 896 } 897 898 /** 899 * Since the actual title bar is embedded in the WebView, and removing it 900 * would change its appearance, create a temporary title bar to go at 901 * the top of the screen while the menu is open. 902 */ 903 private TitleBar mFakeTitleBar; 904 905 /** 906 * Holder for the fake title bar. It will have a foreground shadow, as well 907 * as a white background, so the fake title bar looks like the real one. 908 */ 909 private ViewGroup mFakeTitleBarHolder; 910 911 /** 912 * Layout parameters for the fake title bar within mFakeTitleBarHolder 913 */ 914 private FrameLayout.LayoutParams mFakeTitleBarParams 915 = new FrameLayout.LayoutParams( 916 ViewGroup.LayoutParams.FILL_PARENT, 917 ViewGroup.LayoutParams.WRAP_CONTENT); 918 /** 919 * Keeps track of whether the options menu is open. This is important in 920 * determining whether to show or hide the title bar overlay. 921 */ 922 private boolean mOptionsMenuOpen; 923 924 /** 925 * Only meaningful when mOptionsMenuOpen is true. This variable keeps track 926 * of whether the configuration has changed. The first onMenuOpened call 927 * after a configuration change is simply a reopening of the same menu 928 * (i.e. mIconView did not change). 929 */ 930 private boolean mConfigChanged; 931 932 /** 933 * Whether or not the options menu is in its smaller, icon menu form. When 934 * true, we want the title bar overlay to be up. When false, we do not. 935 * Only meaningful if mOptionsMenuOpen is true. 936 */ 937 private boolean mIconView; 938 939 @Override 940 public boolean onMenuOpened(int featureId, Menu menu) { 941 if (Window.FEATURE_OPTIONS_PANEL == featureId) { 942 if (mOptionsMenuOpen) { 943 if (mConfigChanged) { 944 // We do not need to make any changes to the state of the 945 // title bar, since the only thing that happened was a 946 // change in orientation 947 mConfigChanged = false; 948 } else { 949 if (mIconView) { 950 // Switching the menu to expanded view, so hide the 951 // title bar. 952 hideFakeTitleBar(); 953 mIconView = false; 954 } else { 955 // Switching the menu back to icon view, so show the 956 // title bar once again. 957 showFakeTitleBar(); 958 mIconView = true; 959 } 960 } 961 } else { 962 // The options menu is closed, so open it, and show the title 963 showFakeTitleBar(); 964 mOptionsMenuOpen = true; 965 mConfigChanged = false; 966 mIconView = true; 967 } 968 } 969 return true; 970 } 971 972 /** 973 * Special class used exclusively for the shadow drawn underneath the fake 974 * title bar. The shadow does not need to be drawn if the WebView 975 * underneath is scrolled to the top, because it will draw directly on top 976 * of the embedded shadow. 977 */ 978 private static class Shadow extends View { 979 private WebView mWebView; 980 981 public Shadow(Context context, AttributeSet attrs) { 982 super(context, attrs); 983 } 984 985 public void setWebView(WebView view) { 986 mWebView = view; 987 } 988 989 @Override 990 public void draw(Canvas canvas) { 991 // In general onDraw is the method to override, but we care about 992 // whether or not the background gets drawn, which happens in draw() 993 if (mWebView == null || mWebView.getScrollY() > getHeight()) { 994 super.draw(canvas); 995 } 996 // Need to invalidate so that if the scroll position changes, we 997 // still draw as appropriate. 998 invalidate(); 999 } 1000 } 1001 1002 private void showFakeTitleBar() { 1003 final View decor = getWindow().peekDecorView(); 1004 if (mFakeTitleBar == null && mActiveTabsPage == null 1005 && !mActivityInPause && decor != null 1006 && decor.getWindowToken() != null) { 1007 Rect visRect = new Rect(); 1008 if (!mBrowserFrameLayout.getGlobalVisibleRect(visRect)) { 1009 if (LOGD_ENABLED) { 1010 Log.d(LOGTAG, "showFakeTitleBar visRect failed"); 1011 } 1012 return; 1013 } 1014 final WebView webView = getTopWindow(); 1015 mFakeTitleBar = new TitleBar(this); 1016 mFakeTitleBar.setTitleAndUrl(null, webView.getUrl()); 1017 mFakeTitleBar.setProgress(webView.getProgress()); 1018 mFakeTitleBar.setFavicon(webView.getFavicon()); 1019 updateLockIconToLatest(); 1020 1021 WindowManager manager 1022 = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 1023 1024 // Add the title bar to the window manager so it can receive touches 1025 // while the menu is up 1026 WindowManager.LayoutParams params 1027 = new WindowManager.LayoutParams( 1028 ViewGroup.LayoutParams.FILL_PARENT, 1029 ViewGroup.LayoutParams.WRAP_CONTENT, 1030 WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL, 1031 WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE, 1032 PixelFormat.TRANSLUCENT); 1033 params.gravity = Gravity.TOP; 1034 WebView mainView = mTabControl.getCurrentWebView(); 1035 boolean atTop = mainView != null && mainView.getScrollY() == 0; 1036 params.windowAnimations = atTop ? 0 : R.style.TitleBar; 1037 // XXX : Without providing an offset, the fake title bar will be 1038 // placed underneath the status bar. Use the global visible rect 1039 // of mBrowserFrameLayout to determine the bottom of the status bar 1040 params.y = visRect.top; 1041 // Add a holder for the title bar. It also holds a shadow to show 1042 // below the title bar. 1043 if (mFakeTitleBarHolder == null) { 1044 mFakeTitleBarHolder = (ViewGroup) LayoutInflater.from(this) 1045 .inflate(R.layout.title_bar_bg, null); 1046 } 1047 Shadow shadow = (Shadow) mFakeTitleBarHolder.findViewById( 1048 R.id.shadow); 1049 shadow.setWebView(mainView); 1050 mFakeTitleBarHolder.addView(mFakeTitleBar, 0, mFakeTitleBarParams); 1051 manager.addView(mFakeTitleBarHolder, params); 1052 } 1053 } 1054 1055 @Override 1056 public void onOptionsMenuClosed(Menu menu) { 1057 mOptionsMenuOpen = false; 1058 if (!mInLoad) { 1059 hideFakeTitleBar(); 1060 } else if (!mIconView) { 1061 // The page is currently loading, and we are in expanded mode, so 1062 // we were not showing the menu. Show it once again. It will be 1063 // removed when the page finishes. 1064 showFakeTitleBar(); 1065 } 1066 } 1067 private void hideFakeTitleBar() { 1068 if (mFakeTitleBar == null) return; 1069 WindowManager.LayoutParams params = (WindowManager.LayoutParams) 1070 mFakeTitleBarHolder.getLayoutParams(); 1071 WebView mainView = mTabControl.getCurrentWebView(); 1072 // Although we decided whether or not to animate based on the current 1073 // scroll position, the scroll position may have changed since the 1074 // fake title bar was displayed. Make sure it has the appropriate 1075 // animation/lack thereof before removing. 1076 params.windowAnimations = mainView != null && mainView.getScrollY() == 0 1077 ? 0 : R.style.TitleBar; 1078 WindowManager manager 1079 = (WindowManager) getSystemService(Context.WINDOW_SERVICE); 1080 manager.updateViewLayout(mFakeTitleBarHolder, params); 1081 mFakeTitleBarHolder.removeView(mFakeTitleBar); 1082 manager.removeView(mFakeTitleBarHolder); 1083 mFakeTitleBar = null; 1084 } 1085 1086 /** 1087 * Special method for the fake title bar to call when displaying its context 1088 * menu, since it is in its own Window, and its parent does not show a 1089 * context menu. 1090 */ 1091 /* package */ void showTitleBarContextMenu() { 1092 if (null == mTitleBar.getParent()) { 1093 return; 1094 } 1095 openContextMenu(mTitleBar); 1096 } 1097 1098 /** 1099 * onSaveInstanceState(Bundle map) 1100 * onSaveInstanceState is called right before onStop(). The map contains 1101 * the saved state. 1102 */ 1103 @Override protected void onSaveInstanceState(Bundle outState) { 1104 if (LOGV_ENABLED) { 1105 Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this); 1106 } 1107 // the default implementation requires each view to have an id. As the 1108 // browser handles the state itself and it doesn't use id for the views, 1109 // don't call the default implementation. Otherwise it will trigger the 1110 // warning like this, "couldn't save which view has focus because the 1111 // focused view XXX has no id". 1112 1113 // Save all the tabs 1114 mTabControl.saveState(outState); 1115 } 1116 1117 @Override protected void onPause() { 1118 super.onPause(); 1119 1120 if (mActivityInPause) { 1121 Log.e(LOGTAG, "BrowserActivity is already paused."); 1122 return; 1123 } 1124 1125 mTabControl.pauseCurrentTab(); 1126 mActivityInPause = true; 1127 if (mTabControl.getCurrentIndex() >= 0 && !pauseWebViewTimers()) { 1128 mWakeLock.acquire(); 1129 mHandler.sendMessageDelayed(mHandler 1130 .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT); 1131 } 1132 1133 // Clear the credentials toast if it is up 1134 if (mCredsDlg != null && mCredsDlg.isShowing()) { 1135 mCredsDlg.dismiss(); 1136 } 1137 mCredsDlg = null; 1138 1139 // FIXME: This removes the active tabs page and resets the menu to 1140 // MAIN_MENU. A better solution might be to do this work in onNewIntent 1141 // but then we would need to save it in onSaveInstanceState and restore 1142 // it in onCreate/onRestoreInstanceState 1143 if (mActiveTabsPage != null) { 1144 removeActiveTabPage(true); 1145 } 1146 1147 cancelStopToast(); 1148 1149 // unregister network state listener 1150 unregisterReceiver(mNetworkStateIntentReceiver); 1151 WebView.disablePlatformNotifications(); 1152 1153 if (mSensorManager != null) { 1154 mSensorManager.unregisterListener(mSensorListener); 1155 } 1156 } 1157 1158 @Override protected void onDestroy() { 1159 if (LOGV_ENABLED) { 1160 Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this); 1161 } 1162 super.onDestroy(); 1163 1164 if (mTabControl == null) return; 1165 1166 // Remove the current tab and sub window 1167 TabControl.Tab t = mTabControl.getCurrentTab(); 1168 if (t != null) { 1169 dismissSubWindow(t); 1170 removeTabFromContentView(t); 1171 } 1172 // Destroy all the tabs 1173 mTabControl.destroy(); 1174 WebIconDatabase.getInstance().close(); 1175 if (mGlsConnection != null) { 1176 unbindService(mGlsConnection); 1177 mGlsConnection = null; 1178 } 1179 1180 // 1181 // stop MASF proxy service 1182 // 1183 //Intent proxyServiceIntent = new Intent(); 1184 //proxyServiceIntent.setComponent 1185 // (new ComponentName( 1186 // "com.android.masfproxyservice", 1187 // "com.android.masfproxyservice.MasfProxyService")); 1188 //stopService(proxyServiceIntent); 1189 1190 unregisterReceiver(mPackageInstallationReceiver); 1191 } 1192 1193 @Override 1194 public void onConfigurationChanged(Configuration newConfig) { 1195 mConfigChanged = true; 1196 super.onConfigurationChanged(newConfig); 1197 1198 if (mPageInfoDialog != null) { 1199 mPageInfoDialog.dismiss(); 1200 showPageInfo( 1201 mPageInfoView, 1202 mPageInfoFromShowSSLCertificateOnError.booleanValue()); 1203 } 1204 if (mSSLCertificateDialog != null) { 1205 mSSLCertificateDialog.dismiss(); 1206 showSSLCertificate( 1207 mSSLCertificateView); 1208 } 1209/* 1210 if (mSSLCertificateOnErrorDialog != null) { 1211 mSSLCertificateOnErrorDialog.dismiss(); 1212 showSSLCertificateOnError( 1213 mSSLCertificateOnErrorView, 1214 mSSLCertificateOnErrorHandler, 1215 mSSLCertificateOnErrorError); 1216 } 1217*/ 1218 if (mHttpAuthenticationDialog != null) { 1219 String title = ""; 1220// String title = ((TextView) mHttpAuthenticationDialog 1221// .findViewById(com.android.internal.R.id.alertTitle)).getText() 1222// .toString(); 1223 String name = ((TextView) mHttpAuthenticationDialog 1224 .findViewById(R.id.username_edit)).getText().toString(); 1225 String password = ((TextView) mHttpAuthenticationDialog 1226 .findViewById(R.id.password_edit)).getText().toString(); 1227 int focusId = mHttpAuthenticationDialog.getCurrentFocus() 1228 .getId(); 1229 …
Large files files are truncated, but you can click here to view the full file