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 package com.android.browser; 18 19 import android.app.Activity; 20 import android.app.DownloadManager; 21 import android.app.SearchManager; 22 import android.content.ClipboardManager; 23 import android.content.ContentProvider; 24 import android.content.ContentProviderClient; 25 import android.content.ContentResolver; 26 import android.content.ContentUris; 27 import android.content.ContentValues; 28 import android.content.Context; 29 import android.content.Intent; 30 import android.content.pm.PackageManager; 31 import android.content.pm.ResolveInfo; 32 import android.content.res.Configuration; 33 import android.content.res.TypedArray; 34 import android.database.ContentObserver; 35 import android.database.Cursor; 36 import android.database.sqlite.SQLiteDatabase; 37 import android.graphics.Bitmap; 38 import android.graphics.Canvas; 39 import android.net.Uri; 40 import android.net.http.SslError; 41 import android.os.AsyncTask; 42 import android.os.Bundle; 43 import android.os.Environment; 44 import android.os.Handler; 45 import android.os.Message; 46 import android.os.PowerManager; 47 import android.os.PowerManager.WakeLock; 48 import android.preference.PreferenceActivity; 49 import android.provider.Browser; 50 import android.provider.BrowserContract; 51 import android.provider.BrowserContract.Images; 52 import android.provider.ContactsContract; 53 import android.provider.ContactsContract.Intents.Insert; 54 import android.speech.RecognizerIntent; 55 import android.text.TextUtils; 56 import android.util.Log; 57 import android.util.Patterns; 58 import android.view.ActionMode; 59 import android.view.ContextMenu; 60 import android.view.ContextMenu.ContextMenuInfo; 61 import android.view.Gravity; 62 import android.view.KeyEvent; 63 import android.view.Menu; 64 import android.view.MenuInflater; 65 import android.view.MenuItem; 66 import android.view.MenuItem.OnMenuItemClickListener; 67 import android.view.MotionEvent; 68 import android.view.View; 69 import android.webkit.CookieManager; 70 import android.webkit.CookieSyncManager; 71 import android.webkit.HttpAuthHandler; 72 import android.webkit.MimeTypeMap; 73 import android.webkit.SslErrorHandler; 74 import android.webkit.ValueCallback; 75 import android.webkit.WebChromeClient; 76 import android.webkit.WebIconDatabase; 77 import android.webkit.WebSettings; 78 import android.webkit.WebView; 79 import android.widget.Toast; 80 81 import com.android.browser.IntentHandler.UrlData; 82 import com.android.browser.UI.ComboViews; 83 import com.android.browser.provider.BrowserProvider; 84 import com.android.browser.provider.BrowserProvider2.Thumbnails; 85 import com.android.browser.provider.SnapshotProvider.Snapshots; 86 import com.android.browser.search.SearchEngine; 87 import com.android.common.Search; 88 89 import java.io.ByteArrayOutputStream; 90 import java.io.File; 91 import java.io.FileOutputStream; 92 import java.io.IOException; 93 import java.net.URLEncoder; 94 import java.text.DateFormat; 95 import java.text.SimpleDateFormat; 96 import java.util.ArrayList; 97 import java.util.Calendar; 98 import java.util.Date; 99 import java.util.HashMap; 100 import java.util.List; 101 import java.util.Map; 102 103 /** 104 * Controller for browser 105 */ 106 public class Controller 107 implements WebViewController, UiController { 108 109 private static final String LOGTAG = "Controller"; 110 private static final String SEND_APP_ID_EXTRA = 111 "android.speech.extras.SEND_APPLICATION_ID_EXTRA"; 112 private static final String INCOGNITO_URI = "browser:incognito"; 113 114 115 // public message ids 116 public final static int LOAD_URL = 1001; 117 public final static int STOP_LOAD = 1002; 118 119 // Message Ids 120 private static final int FOCUS_NODE_HREF = 102; 121 private static final int RELEASE_WAKELOCK = 107; 122 123 static final int UPDATE_BOOKMARK_THUMBNAIL = 108; 124 125 private static final int OPEN_BOOKMARKS = 201; 126 127 private static final int EMPTY_MENU = -1; 128 129 // activity requestCode 130 final static int COMBO_VIEW = 1; 131 final static int PREFERENCES_PAGE = 3; 132 final static int FILE_SELECTED = 4; 133 final static int AUTOFILL_SETUP = 5; 134 135 private final static int WAKELOCK_TIMEOUT = 5 * 60 * 1000; // 5 minutes 136 137 // As the ids are dynamically created, we can't guarantee that they will 138 // be in sequence, so this static array maps ids to a window number. 139 final static private int[] WINDOW_SHORTCUT_ID_ARRAY = 140 { R.id.window_one_menu_id, R.id.window_two_menu_id, 141 R.id.window_three_menu_id, R.id.window_four_menu_id, 142 R.id.window_five_menu_id, R.id.window_six_menu_id, 143 R.id.window_seven_menu_id, R.id.window_eight_menu_id }; 144 145 // "source" parameter for Google search through search key 146 final static String GOOGLE_SEARCH_SOURCE_SEARCHKEY = "browser-key"; 147 // "source" parameter for Google search through simplily type 148 final static String GOOGLE_SEARCH_SOURCE_TYPE = "browser-type"; 149 150 // "no-crash-recovery" parameter in intent to suppress crash recovery 151 final static String NO_CRASH_RECOVERY = "no-crash-recovery"; 152 153 // A bitmap that is re-used in createScreenshot as scratch space 154 private static Bitmap sThumbnailBitmap; 155 156 private Activity mActivity; 157 private UI mUi; 158 private TabControl mTabControl; 159 private BrowserSettings mSettings; 160 private WebViewFactory mFactory; 161 162 private WakeLock mWakeLock; 163 164 private UrlHandler mUrlHandler; 165 private UploadHandler mUploadHandler; 166 private IntentHandler mIntentHandler; 167 private PageDialogsHandler mPageDialogsHandler; 168 private NetworkStateHandler mNetworkHandler; 169 170 private Message mAutoFillSetupMessage; 171 172 private boolean mShouldShowErrorConsole; 173 174 private SystemAllowGeolocationOrigins mSystemAllowGeolocationOrigins; 175 176 // FIXME, temp address onPrepareMenu performance problem. 177 // When we move everything out of view, we should rewrite this. 178 private int mCurrentMenuState = 0; 179 private int mMenuState = R.id.MAIN_MENU; 180 private int mOldMenuState = EMPTY_MENU; 181 private Menu mCachedMenu; 182 183 private boolean mMenuIsDown; 184 185 // For select and find, we keep track of the ActionMode so that 186 // finish() can be called as desired. 187 private ActionMode mActionMode; 188 189 /** 190 * Only meaningful when mOptionsMenuOpen is true. This variable keeps track 191 * of whether the configuration has changed. The first onMenuOpened call 192 * after a configuration change is simply a reopening of the same menu 193 * (i.e. mIconView did not change). 194 */ 195 private boolean mConfigChanged; 196 197 /** 198 * Keeps track of whether the options menu is open. This is important in 199 * determining whether to show or hide the title bar overlay 200 */ 201 private boolean mOptionsMenuOpen; 202 203 /** 204 * Whether or not the options menu is in its bigger, popup menu form. When 205 * true, we want the title bar overlay to be gone. When false, we do not. 206 * Only meaningful if mOptionsMenuOpen is true. 207 */ 208 private boolean mExtendedMenuOpen; 209 210 private boolean mActivityPaused = true; 211 private boolean mLoadStopped; 212 213 private Handler mHandler; 214 // Checks to see when the bookmarks database has changed, and updates the 215 // Tabs' notion of whether they represent bookmarked sites. 216 private ContentObserver mBookmarksObserver; 217 private CrashRecoveryHandler mCrashRecoveryHandler; 218 219 private boolean mBlockEvents; 220 221 public Controller(Activity browser, boolean preloadCrashState) { 222 mActivity = browser; 223 mSettings = BrowserSettings.getInstance(); 224 mTabControl = new TabControl(this); 225 mSettings.setController(this); 226 mCrashRecoveryHandler = CrashRecoveryHandler.initialize(this); 227 if (preloadCrashState) { 228 mCrashRecoveryHandler.preloadCrashState(); 229 } 230 mFactory = new BrowserWebViewFactory(browser); 231 232 mUrlHandler = new UrlHandler(this); 233 mIntentHandler = new IntentHandler(mActivity, this); 234 mPageDialogsHandler = new PageDialogsHandler(mActivity, this); 235 236 startHandler(); 237 mBookmarksObserver = new ContentObserver(mHandler) { 238 @Override 239 public void onChange(boolean selfChange) { 240 int size = mTabControl.getTabCount(); 241 for (int i = 0; i < size; i++) { 242 mTabControl.getTab(i).updateBookmarkedStatus(); 243 } 244 } 245 246 }; 247 browser.getContentResolver().registerContentObserver( 248 BrowserContract.Bookmarks.CONTENT_URI, true, mBookmarksObserver); 249 250 mNetworkHandler = new NetworkStateHandler(mActivity, this); 251 // Start watching the default geolocation permissions 252 mSystemAllowGeolocationOrigins = 253 new SystemAllowGeolocationOrigins(mActivity.getApplicationContext()); 254 mSystemAllowGeolocationOrigins.start(); 255 256 openIconDatabase(); 257 } 258 259 void start(final Bundle icicle, final Intent intent) { 260 boolean noCrashRecovery = intent.getBooleanExtra(NO_CRASH_RECOVERY, false); 261 if (icicle != null || noCrashRecovery) { 262 doStart(icicle, intent, false); 263 } else { 264 mCrashRecoveryHandler.startRecovery(intent); 265 } 266 } 267 268 void doStart(final Bundle icicle, final Intent intent, final boolean fromCrash) { 269 // Unless the last browser usage was within 24 hours, destroy any 270 // remaining incognito tabs. 271 272 Calendar lastActiveDate = icicle != null ? 273 (Calendar) icicle.getSerializable("lastActiveDate") : null; 274 Calendar today = Calendar.getInstance(); 275 Calendar yesterday = Calendar.getInstance(); 276 yesterday.add(Calendar.DATE, -1); 277 278 final boolean restoreIncognitoTabs = !(lastActiveDate == null 279 || lastActiveDate.before(yesterday) 280 || lastActiveDate.after(today)); 281 282 // Find out if we will restore any state and remember the tab. 283 final long currentTabId = 284 mTabControl.canRestoreState(icicle, restoreIncognitoTabs); 285 286 if (currentTabId == -1) { 287 // Not able to restore so we go ahead and clear session cookies. We 288 // must do this before trying to login the user as we don't want to 289 // clear any session cookies set during login. 290 CookieManager.getInstance().removeSessionCookie(); 291 } 292 293 GoogleAccountLogin.startLoginIfNeeded(mActivity, 294 new Runnable() { 295 @Override public void run() { 296 onPreloginFinished(icicle, intent, currentTabId, restoreIncognitoTabs, 297 fromCrash); 298 } 299 }); 300 } 301 302 private void onPreloginFinished(Bundle icicle, Intent intent, long currentTabId, 303 boolean restoreIncognitoTabs, boolean fromCrash) { 304 if (currentTabId == -1) { 305 BackgroundHandler.execute(new PruneThumbnails(mActivity, null)); 306 final Bundle extra = intent.getExtras(); 307 // Create an initial tab. 308 // If the intent is ACTION_VIEW and data is not null, the Browser is 309 // invoked to view the content by another application. In this case, 310 // the tab will be close when exit. 311 UrlData urlData = IntentHandler.getUrlDataFromIntent(intent); 312 Tab t = null; 313 if (urlData.isEmpty()) { 314 t = openTabToHomePage(); 315 } else { 316 t = openTab(urlData); 317 } 318 if (t != null) { 319 t.setAppId(intent.getStringExtra(Browser.EXTRA_APPLICATION_ID)); 320 } 321 WebView webView = t.getWebView(); 322 if (extra != null) { 323 int scale = extra.getInt(Browser.INITIAL_ZOOM_LEVEL, 0); 324 if (scale > 0 && scale <= 1000) { 325 webView.setInitialScale(scale); 326 } 327 } 328 mUi.updateTabs(mTabControl.getTabs()); 329 } else { 330 mTabControl.restoreState(icicle, currentTabId, restoreIncognitoTabs, 331 mUi.needsRestoreAllTabs()); 332 List<Tab> tabs = mTabControl.getTabs(); 333 ArrayList<Long> restoredTabs = new ArrayList<Long>(tabs.size()); 334 for (Tab t : tabs) { 335 restoredTabs.add(t.getId()); 336 } 337 BackgroundHandler.execute(new PruneThumbnails(mActivity, restoredTabs)); 338 if (tabs.size() == 0) { 339 openTabToHomePage(); 340 } 341 mUi.updateTabs(tabs); 342 // TabControl.restoreState() will create a new tab even if 343 // restoring the state fails. 344 setActiveTab(mTabControl.getCurrentTab()); 345 // Handle the intent if needed. If icicle != null, we are restoring 346 // and the intent will be stale - ignore it. 347 if (icicle == null || fromCrash) { 348 mIntentHandler.onNewIntent(intent); 349 } 350 } 351 // Read JavaScript flags if it exists. 352 String jsFlags = getSettings().getJsEngineFlags(); 353 if (jsFlags.trim().length() != 0) { 354 getCurrentWebView().setJsFlags(jsFlags); 355 } 356 if (BrowserActivity.ACTION_SHOW_BOOKMARKS.equals(intent.getAction())) { 357 bookmarksOrHistoryPicker(ComboViews.Bookmarks); 358 } 359 } 360 361 private static class PruneThumbnails implements Runnable { 362 private Context mContext; 363 private List<Long> mIds; 364 365 PruneThumbnails(Context context, List<Long> preserveIds) { 366 mContext = context.getApplicationContext(); 367 mIds = preserveIds; 368 } 369 370 @Override 371 public void run() { 372 ContentResolver cr = mContext.getContentResolver(); 373 if (mIds == null || mIds.size() == 0) { 374 cr.delete(Thumbnails.CONTENT_URI, null, null); 375 } else { 376 int length = mIds.size(); 377 StringBuilder where = new StringBuilder(); 378 where.append(Thumbnails._ID); 379 where.append(" not in ("); 380 for (int i = 0; i < length; i++) { 381 where.append(mIds.get(i)); 382 if (i < (length - 1)) { 383 where.append(","); 384 } 385 } 386 where.append(")"); 387 cr.delete(Thumbnails.CONTENT_URI, where.toString(), null); 388 } 389 } 390 391 } 392 393 @Override 394 public WebViewFactory getWebViewFactory() { 395 return mFactory; 396 } 397 398 @Override 399 public void onSetWebView(Tab tab, WebView view) { 400 mUi.onSetWebView(tab, view); 401 } 402 403 @Override 404 public void createSubWindow(Tab tab) { 405 endActionMode(); 406 WebView mainView = tab.getWebView(); 407 WebView subView = mFactory.createWebView((mainView == null) 408 ? false 409 : mainView.isPrivateBrowsingEnabled()); 410 mUi.createSubWindow(tab, subView); 411 } 412 413 @Override 414 public Context getContext() { 415 return mActivity; 416 } 417 418 @Override 419 public Activity getActivity() { 420 return mActivity; 421 } 422 423 void setUi(UI ui) { 424 mUi = ui; 425 } 426 427 BrowserSettings getSettings() { 428 return mSettings; 429 } 430 431 IntentHandler getIntentHandler() { 432 return mIntentHandler; 433 } 434 435 @Override 436 public UI getUi() { 437 return mUi; 438 } 439 440 int getMaxTabs() { 441 return mActivity.getResources().getInteger(R.integer.max_tabs); 442 } 443 444 @Override 445 public TabControl getTabControl() { 446 return mTabControl; 447 } 448 449 @Override 450 public List<Tab> getTabs() { 451 return mTabControl.getTabs(); 452 } 453 454 // Open the icon database. 455 private void openIconDatabase() { 456 // We have to call getInstance on the UI thread 457 final WebIconDatabase instance = WebIconDatabase.getInstance(); 458 BackgroundHandler.execute(new Runnable() { 459 460 @Override 461 public void run() { 462 instance.open(mActivity.getDir("icons", 0).getPath()); 463 } 464 }); 465 } 466 467 private void startHandler() { 468 mHandler = new Handler() { 469 470 @Override 471 public void handleMessage(Message msg) { 472 switch (msg.what) { 473 case OPEN_BOOKMARKS: 474 bookmarksOrHistoryPicker(ComboViews.Bookmarks); 475 break; 476 case FOCUS_NODE_HREF: 477 { 478 String url = (String) msg.getData().get("url"); 479 String title = (String) msg.getData().get("title"); 480 String src = (String) msg.getData().get("src"); 481 if (url == "") url = src; // use image if no anchor 482 if (TextUtils.isEmpty(url)) { 483 break; 484 } 485 HashMap focusNodeMap = (HashMap) msg.obj; 486 WebView view = (WebView) focusNodeMap.get("webview"); 487 // Only apply the action if the top window did not change. 488 if (getCurrentTopWebView() != view) { 489 break; 490 } 491 switch (msg.arg1) { 492 case R.id.open_context_menu_id: 493 loadUrlFromContext(url); 494 break; 495 case R.id.view_image_context_menu_id: 496 loadUrlFromContext(src); 497 break; 498 case R.id.open_newtab_context_menu_id: 499 final Tab parent = mTabControl.getCurrentTab(); 500 openTab(url, parent, 501 !mSettings.openInBackground(), true); 502 break; 503 case R.id.copy_link_context_menu_id: 504 copy(url); 505 break; 506 case R.id.save_link_context_menu_id: 507 case R.id.download_context_menu_id: 508 DownloadHandler.onDownloadStartNoStream( 509 mActivity, url, null, null, null, 510 view.isPrivateBrowsingEnabled()); 511 break; 512 } 513 break; 514 } 515 516 case LOAD_URL: 517 loadUrlFromContext((String) msg.obj); 518 break; 519 520 case STOP_LOAD: 521 stopLoading(); 522 break; 523 524 case RELEASE_WAKELOCK: 525 if (mWakeLock != null && mWakeLock.isHeld()) { 526 mWakeLock.release(); 527 // if we reach here, Browser should be still in the 528 // background loading after WAKELOCK_TIMEOUT (5-min). 529 // To avoid burning the battery, stop loading. 530 mTabControl.stopAllLoading(); 531 } 532 break; 533 534 case UPDATE_BOOKMARK_THUMBNAIL: 535 Tab tab = (Tab) msg.obj; 536 if (tab != null) { 537 updateScreenshot(tab); 538 } 539 break; 540 } 541 } 542 }; 543 544 } 545 546 @Override 547 public Tab getCurrentTab() { 548 return mTabControl.getCurrentTab(); 549 } 550 551 @Override 552 public void shareCurrentPage() { 553 shareCurrentPage(mTabControl.getCurrentTab()); 554 } 555 556 private void shareCurrentPage(Tab tab) { 557 if (tab != null) { 558 sharePage(mActivity, tab.getTitle(), 559 tab.getUrl(), tab.getFavicon(), 560 createScreenshot(tab.getWebView(), 561 getDesiredThumbnailWidth(mActivity), 562 getDesiredThumbnailHeight(mActivity))); 563 } 564 } 565 566 /** 567 * Share a page, providing the title, url, favicon, and a screenshot. Uses 568 * an {@link Intent} to launch the Activity chooser. 569 * @param c Context used to launch a new Activity. 570 * @param title Title of the page. Stored in the Intent with 571 * {@link Intent#EXTRA_SUBJECT} 572 * @param url URL of the page. Stored in the Intent with 573 * {@link Intent#EXTRA_TEXT} 574 * @param favicon Bitmap of the favicon for the page. Stored in the Intent 575 * with {@link Browser#EXTRA_SHARE_FAVICON} 576 * @param screenshot Bitmap of a screenshot of the page. Stored in the 577 * Intent with {@link Browser#EXTRA_SHARE_SCREENSHOT} 578 */ 579 static final void sharePage(Context c, String title, String url, 580 Bitmap favicon, Bitmap screenshot) { 581 Intent send = new Intent(Intent.ACTION_SEND); 582 send.setType("text/plain"); 583 send.putExtra(Intent.EXTRA_TEXT, url); 584 send.putExtra(Intent.EXTRA_SUBJECT, title); 585 send.putExtra(Browser.EXTRA_SHARE_FAVICON, favicon); 586 send.putExtra(Browser.EXTRA_SHARE_SCREENSHOT, screenshot); 587 try { 588 c.startActivity(Intent.createChooser(send, c.getString( 589 R.string.choosertitle_sharevia))); 590 } catch(android.content.ActivityNotFoundException ex) { 591 // if no app handles it, do nothing 592 } 593 } 594 595 private void copy(CharSequence text) { 596 ClipboardManager cm = (ClipboardManager) mActivity 597 .getSystemService(Context.CLIPBOARD_SERVICE); 598 cm.setText(text); 599 } 600 601 // lifecycle 602 603 protected void onConfgurationChanged(Configuration config) { 604 mConfigChanged = true; 605 if (mPageDialogsHandler != null) { 606 mPageDialogsHandler.onConfigurationChanged(config); 607 } 608 mUi.onConfigurationChanged(config); 609 } 610 611 @Override 612 public void handleNewIntent(Intent intent) { 613 if (!mUi.isWebShowing()) { 614 mUi.showWeb(false); 615 } 616 mIntentHandler.onNewIntent(intent); 617 } 618 619 protected void onPause() { 620 if (mUi.isCustomViewShowing()) { 621 hideCustomView(); 622 } 623 if (mActivityPaused) { 624 Log.e(LOGTAG, "BrowserActivity is already paused."); 625 return; 626 } 627 mActivityPaused = true; 628 Tab tab = mTabControl.getCurrentTab(); 629 if (tab != null) { 630 tab.pause(); 631 if (!pauseWebViewTimers(tab)) { 632 if (mWakeLock == null) { 633 PowerManager pm = (PowerManager) mActivity 634 .getSystemService(Context.POWER_SERVICE); 635 mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser"); 636 } 637 mWakeLock.acquire(); 638 mHandler.sendMessageDelayed(mHandler 639 .obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT); 640 } 641 } 642 mUi.onPause(); 643 mNetworkHandler.onPause(); 644 645 WebView.disablePlatformNotifications(); 646 NfcHandler.unregister(mActivity); 647 if (sThumbnailBitmap != null) { 648 sThumbnailBitmap.recycle(); 649 sThumbnailBitmap = null; 650 } 651 } 652 653 void onSaveInstanceState(Bundle outState) { 654 // the default implementation requires each view to have an id. As the 655 // browser handles the state itself and it doesn't use id for the views, 656 // don't call the default implementation. Otherwise it will trigger the 657 // warning like this, "couldn't save which view has focus because the 658 // focused view XXX has no id". 659 660 // Save all the tabs 661 mTabControl.saveState(outState); 662 if (!outState.isEmpty()) { 663 // Save time so that we know how old incognito tabs (if any) are. 664 outState.putSerializable("lastActiveDate", Calendar.getInstance()); 665 } 666 } 667 668 void onResume() { 669 if (!mActivityPaused) { 670 Log.e(LOGTAG, "BrowserActivity is already resumed."); 671 return; 672 } 673 mActivityPaused = false; 674 Tab current = mTabControl.getCurrentTab(); 675 if (current != null) { 676 current.resume(); 677 resumeWebViewTimers(current); 678 } 679 releaseWakeLock(); 680 681 mUi.onResume(); 682 mNetworkHandler.onResume(); 683 WebView.enablePlatformNotifications(); 684 NfcHandler.register(mActivity, this); 685 } 686 687 private void releaseWakeLock() { 688 if (mWakeLock != null && mWakeLock.isHeld()) { 689 mHandler.removeMessages(RELEASE_WAKELOCK); 690 mWakeLock.release(); 691 } 692 } 693 694 /** 695 * resume all WebView timers using the WebView instance of the given tab 696 * @param tab guaranteed non-null 697 */ 698 private void resumeWebViewTimers(Tab tab) { 699 boolean inLoad = tab.inPageLoad(); 700 if ((!mActivityPaused && !inLoad) || (mActivityPaused && inLoad)) { 701 CookieSyncManager.getInstance().startSync(); 702 WebView w = tab.getWebView(); 703 WebViewTimersControl.getInstance().onBrowserActivityResume(w); 704 } 705 } 706 707 /** 708 * Pause all WebView timers using the WebView of the given tab 709 * @param tab 710 * @return true if the timers are paused or tab is null 711 */ 712 private boolean pauseWebViewTimers(Tab tab) { 713 if (tab == null) { 714 return true; 715 } else if (!tab.inPageLoad()) { 716 CookieSyncManager.getInstance().stopSync(); 717 WebViewTimersControl.getInstance().onBrowserActivityPause(getCurrentWebView()); 718 return true; 719 } 720 return false; 721 } 722 723 void onDestroy() { 724 if (mUploadHandler != null && !mUploadHandler.handled()) { 725 mUploadHandler.onResult(Activity.RESULT_CANCELED, null); 726 mUploadHandler = null; 727 } 728 if (mTabControl == null) return; 729 mUi.onDestroy(); 730 // Remove the current tab and sub window 731 Tab t = mTabControl.getCurrentTab(); 732 if (t != null) { 733 dismissSubWindow(t); 734 removeTab(t); 735 } 736 mActivity.getContentResolver().unregisterContentObserver(mBookmarksObserver); 737 // Destroy all the tabs 738 mTabControl.destroy(); 739 WebIconDatabase.getInstance().close(); 740 // Stop watching the default geolocation permissions 741 mSystemAllowGeolocationOrigins.stop(); 742 mSystemAllowGeolocationOrigins = null; 743 } 744 745 protected boolean isActivityPaused() { 746 return mActivityPaused; 747 } 748 749 protected void onLowMemory() { 750 mTabControl.freeMemory(); 751 } 752 753 @Override 754 public boolean shouldShowErrorConsole() { 755 return mShouldShowErrorConsole; 756 } 757 758 protected void setShouldShowErrorConsole(boolean show) { 759 if (show == mShouldShowErrorConsole) { 760 // Nothing to do. 761 return; 762 } 763 mShouldShowErrorConsole = show; 764 Tab t = mTabControl.getCurrentTab(); 765 if (t == null) { 766 // There is no current tab so we cannot toggle the error console 767 return; 768 } 769 mUi.setShouldShowErrorConsole(t, show); 770 } 771 772 @Override 773 public void stopLoading() { 774 mLoadStopped = true; 775 Tab tab = mTabControl.getCurrentTab(); 776 WebView w = getCurrentTopWebView(); 777 w.stopLoading(); 778 mUi.onPageStopped(tab); 779 } 780 781 boolean didUserStopLoading() { 782 return mLoadStopped; 783 } 784 785 // WebViewController 786 787 @Override 788 public void onPageStarted(Tab tab, WebView view, Bitmap favicon) { 789 790 // We've started to load a new page. If there was a pending message 791 // to save a screenshot then we will now take the new page and save 792 // an incorrect screenshot. Therefore, remove any pending thumbnail 793 // messages from the queue. 794 mHandler.removeMessages(Controller.UPDATE_BOOKMARK_THUMBNAIL, 795 tab); 796 797 // reset sync timer to avoid sync starts during loading a page 798 CookieSyncManager.getInstance().resetSync(); 799 800 if (!mNetworkHandler.isNetworkUp()) { 801 view.setNetworkAvailable(false); 802 } 803 804 // when BrowserActivity just starts, onPageStarted may be called before 805 // onResume as it is triggered from onCreate. Call resumeWebViewTimers 806 // to start the timer. As we won't switch tabs while an activity is in 807 // pause state, we can ensure calling resume and pause in pair. 808 if (mActivityPaused) { 809 resumeWebViewTimers(tab); 810 } 811 mLoadStopped = false; 812 endActionMode(); 813 814 mUi.onTabDataChanged(tab); 815 816 String url = tab.getUrl(); 817 // update the bookmark database for favicon 818 maybeUpdateFavicon(tab, null, url, favicon); 819 820 Performance.tracePageStart(url); 821 822 // Performance probe 823 if (false) { 824 Performance.onPageStarted(); 825 } 826 827 } 828 829 @Override 830 public void onPageFinished(Tab tab) { 831 mUi.onTabDataChanged(tab); 832 if (!tab.isPrivateBrowsingEnabled() 833 && !TextUtils.isEmpty(tab.getUrl()) 834 && !tab.isSnapshot()) { 835 // Only update the bookmark screenshot if the user did not 836 // cancel the load early and there is not already 837 // a pending update for the tab. 838 if (tab.inForeground() && !didUserStopLoading() 839 || !tab.inForeground()) { 840 if (!mHandler.hasMessages(UPDATE_BOOKMARK_THUMBNAIL, tab)) { 841 mHandler.sendMessageDelayed(mHandler.obtainMessage( 842 UPDATE_BOOKMARK_THUMBNAIL, 0, 0, tab), 843 500); 844 } 845 } 846 } 847 // pause the WebView timer and release the wake lock if it is finished 848 // while BrowserActivity is in pause state. 849 if (mActivityPaused && pauseWebViewTimers(tab)) { 850 releaseWakeLock(); 851 } 852 853 // Performance probe 854 if (false) { 855 Performance.onPageFinished(tab.getUrl()); 856 } 857 858 Performance.tracePageFinished(); 859 } 860 861 @Override 862 public void onProgressChanged(Tab tab) { 863 mCrashRecoveryHandler.backupState(); 864 int newProgress = tab.getLoadProgress(); 865 866 if (newProgress == 100) { 867 CookieSyncManager.getInstance().sync(); 868 // onProgressChanged() may continue to be called after the main 869 // frame has finished loading, as any remaining sub frames continue 870 // to load. We'll only get called once though with newProgress as 871 // 100 when everything is loaded. (onPageFinished is called once 872 // when the main frame completes loading regardless of the state of 873 // any sub frames so calls to onProgressChanges may continue after 874 // onPageFinished has executed) 875 if (tab.inPageLoad()) { 876 updateInLoadMenuItems(mCachedMenu, tab); 877 } 878 } else { 879 if (!tab.inPageLoad()) { 880 // onPageFinished may have already been called but a subframe is 881 // still loading 882 // updating the progress and 883 // update the menu items. 884 updateInLoadMenuItems(mCachedMenu, tab); 885 } 886 } 887 mUi.onProgressChanged(tab); 888 } 889 890 @Override 891 public void onUpdatedSecurityState(Tab tab) { 892 mUi.onTabDataChanged(tab); 893 } 894 895 @Override 896 public void onReceivedTitle(Tab tab, final String title) { 897 mUi.onTabDataChanged(tab); 898 final String pageUrl = tab.getOriginalUrl(); 899 if (TextUtils.isEmpty(pageUrl) || pageUrl.length() 900 >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) { 901 return; 902 } 903 // Update the title in the history database if not in private browsing mode 904 if (!tab.isPrivateBrowsingEnabled()) { 905 DataController.getInstance(mActivity).updateHistoryTitle(pageUrl, title); 906 } 907 } 908 909 @Override 910 public void onFavicon(Tab tab, WebView view, Bitmap icon) { 911 mUi.onTabDataChanged(tab); 912 maybeUpdateFavicon(tab, view.getOriginalUrl(), view.getUrl(), icon); 913 } 914 915 @Override 916 public boolean shouldOverrideUrlLoading(Tab tab, WebView view, String url) { 917 return mUrlHandler.shouldOverrideUrlLoading(tab, view, url); 918 } 919 920 @Override 921 public boolean shouldOverrideKeyEvent(KeyEvent event) { 922 if (mMenuIsDown) { 923 // only check shortcut key when MENU is held 924 return mActivity.getWindow().isShortcutKey(event.getKeyCode(), 925 event); 926 } else { 927 return false; 928 } 929 } 930 931 @Override 932 public void onUnhandledKeyEvent(KeyEvent event) { 933 if (!isActivityPaused()) { 934 if (event.getAction() == KeyEvent.ACTION_DOWN) { 935 mActivity.onKeyDown(event.getKeyCode(), event); 936 } else { 937 mActivity.onKeyUp(event.getKeyCode(), event); 938 } 939 } 940 } 941 942 @Override 943 public void doUpdateVisitedHistory(Tab tab, boolean isReload) { 944 // Don't save anything in private browsing mode 945 if (tab.isPrivateBrowsingEnabled()) return; 946 String url = tab.getOriginalUrl(); 947 948 if (TextUtils.isEmpty(url) 949 || url.regionMatches(true, 0, "about:", 0, 6)) { 950 return; 951 } 952 DataController.getInstance(mActivity).updateVisitedHistory(url); 953 mCrashRecoveryHandler.backupState(); 954 } 955 956 @Override 957 public void getVisitedHistory(final ValueCallback<String[]> callback) { 958 AsyncTask<Void, Void, String[]> task = 959 new AsyncTask<Void, Void, String[]>() { 960 @Override 961 public String[] doInBackground(Void... unused) { 962 return Browser.getVisitedHistory(mActivity.getContentResolver()); 963 } 964 @Override 965 public void onPostExecute(String[] result) { 966 callback.onReceiveValue(result); 967 } 968 }; 969 task.execute(); 970 } 971 972 @Override 973 public void onReceivedHttpAuthRequest(Tab tab, WebView view, 974 final HttpAuthHandler handler, final String host, 975 final String realm) { 976 String username = null; 977 String password = null; 978 979 boolean reuseHttpAuthUsernamePassword 980 = handler.useHttpAuthUsernamePassword(); 981 982 if (reuseHttpAuthUsernamePassword && view != null) { 983 String[] credentials = view.getHttpAuthUsernamePassword(host, realm); 984 if (credentials != null && credentials.length == 2) { 985 username = credentials[0]; 986 password = credentials[1]; 987 } 988 } 989 990 if (username != null && password != null) { 991 handler.proceed(username, password); 992 } else { 993 if (tab.inForeground() && !handler.suppressDialog()) { 994 mPageDialogsHandler.showHttpAuthentication(tab, handler, host, realm); 995 } else { 996 handler.cancel(); 997 } 998 } 999 } 1000 1001 @Override 1002 public void onDownloadStart(Tab tab, String url, String userAgent, 1003 String contentDisposition, String mimetype, long contentLength) { 1004 WebView w = tab.getWebView(); 1005 DownloadHandler.onDownloadStart(mActivity, url, userAgent, 1006 contentDisposition, mimetype, w.isPrivateBrowsingEnabled()); 1007 if (w.copyBackForwardList().getSize() == 0) { 1008 // This Tab was opened for the sole purpose of downloading a 1009 // file. Remove it. 1010 if (tab == mTabControl.getCurrentTab()) { 1011 // In this case, the Tab is still on top. 1012 goBackOnePageOrQuit(); 1013 } else { 1014 // In this case, it is not. 1015 closeTab(tab); 1016 } 1017 } 1018 } 1019 1020 @Override 1021 public Bitmap getDefaultVideoPoster() { 1022 return mUi.getDefaultVideoPoster(); 1023 } 1024 1025 @Override 1026 public View getVideoLoadingProgressView() { 1027 return mUi.getVideoLoadingProgressView(); 1028 } 1029 1030 @Override 1031 public void showSslCertificateOnError(WebView view, SslErrorHandler handler, 1032 SslError error) { 1033 mPageDialogsHandler.showSSLCertificateOnError(view, handler, error); 1034 } 1035 1036 @Override 1037 public void showAutoLogin(Tab tab) { 1038 assert tab.inForeground(); 1039 // Update the title bar to show the auto-login request. 1040 mUi.showAutoLogin(tab); 1041 } 1042 1043 @Override 1044 public void hideAutoLogin(Tab tab) { 1045 assert tab.inForeground(); 1046 mUi.hideAutoLogin(tab); 1047 } 1048 1049 // helper method 1050 1051 /* 1052 * Update the favorites icon if the private browsing isn't enabled and the 1053 * icon is valid. 1054 */ 1055 private void maybeUpdateFavicon(Tab tab, final String originalUrl, 1056 final String url, Bitmap favicon) { 1057 if (favicon == null) { 1058 return; 1059 } 1060 if (!tab.isPrivateBrowsingEnabled()) { 1061 Bookmarks.updateFavicon(mActivity 1062 .getContentResolver(), originalUrl, url, favicon); 1063 } 1064 } 1065 1066 @Override 1067 public void bookmarkedStatusHasChanged(Tab tab) { 1068 // TODO: Switch to using onTabDataChanged after b/3262950 is fixed 1069 mUi.bookmarkedStatusHasChanged(tab); 1070 } 1071 1072 // end WebViewController 1073 1074 protected void pageUp() { 1075 getCurrentTopWebView().pageUp(false); 1076 } 1077 1078 protected void pageDown() { 1079 getCurrentTopWebView().pageDown(false); 1080 } 1081 1082 // callback from phone title bar 1083 public void editUrl() { 1084 if (mOptionsMenuOpen) mActivity.closeOptionsMenu(); 1085 mUi.editUrl(false); 1086 } 1087 1088 public void startVoiceSearch() { 1089 Intent intent = new Intent(RecognizerIntent.ACTION_WEB_SEARCH); 1090 intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL, 1091 RecognizerIntent.LANGUAGE_MODEL_WEB_SEARCH); 1092 intent.putExtra(RecognizerIntent.EXTRA_CALLING_PACKAGE, 1093 mActivity.getComponentName().flattenToString()); 1094 intent.putExtra(SEND_APP_ID_EXTRA, false); 1095 intent.putExtra(RecognizerIntent.EXTRA_WEB_SEARCH_ONLY, true); 1096 mActivity.startActivity(intent); 1097 } 1098 1099 @Override 1100 public void activateVoiceSearchMode(String title, List<String> results) { 1101 mUi.showVoiceTitleBar(title, results); 1102 } 1103 1104 public void revertVoiceSearchMode(Tab tab) { 1105 mUi.revertVoiceTitleBar(tab); 1106 } 1107 1108 public boolean supportsVoiceSearch() { 1109 SearchEngine searchEngine = getSettings().getSearchEngine(); 1110 return (searchEngine != null && searchEngine.supportsVoiceSearch()); 1111 } 1112 1113 public void showCustomView(Tab tab, View view, int requestedOrientation, 1114 WebChromeClient.CustomViewCallback callback) { 1115 if (tab.inForeground()) { 1116 if (mUi.isCustomViewShowing()) { 1117 callback.onCustomViewHidden(); 1118 return; 1119 } 1120 mUi.showCustomView(view, requestedOrientation, callback); 1121 // Save the menu state and set it to empty while the custom 1122 // view is showing. 1123 mOldMenuState = mMenuState; 1124 mMenuState = EMPTY_MENU; 1125 mActivity.invalidateOptionsMenu(); 1126 } 1127 } 1128 1129 @Override 1130 public void hideCustomView() { 1131 if (mUi.isCustomViewShowing()) { 1132 mUi.onHideCustomView(); 1133 // Reset the old menu state. 1134 mMenuState = mOldMenuState; 1135 mOldMenuState = EMPTY_MENU; 1136 mActivity.invalidateOptionsMenu(); 1137 } 1138 } 1139 1140 protected void onActivityResult(int requestCode, int resultCode, 1141 Intent intent) { 1142 if (getCurrentTopWebView() == null) return; 1143 switch (requestCode) { 1144 case PREFERENCES_PAGE: 1145 if (resultCode == Activity.RESULT_OK && intent != null) { 1146 String action = intent.getStringExtra(Intent.EXTRA_TEXT); 1147 if (PreferenceKeys.PREF_PRIVACY_CLEAR_HISTORY.equals(action)) { 1148 mTabControl.removeParentChildRelationShips(); 1149 } 1150 } 1151 break; 1152 case FILE_SELECTED: 1153 // Chose a file from the file picker. 1154 if (null == mUploadHandler) break; 1155 mUploadHandler.onResult(resultCode, intent); 1156 break; 1157 case AUTOFILL_SETUP: 1158 // Determine whether a profile was actually set up or not 1159 // and if so, send the message back to the WebTextView to 1160 // fill the form with the new profile. 1161 if (getSettings().getAutoFillProfile() != null) { 1162 mAutoFillSetupMessage.sendToTarget(); 1163 mAutoFillSetupMessage = null; 1164 } 1165 break; 1166 case COMBO_VIEW: 1167 if (intent == null || resultCode != Activity.RESULT_OK) { 1168 break; 1169 } 1170 mUi.showWeb(false); 1171 if (Intent.ACTION_VIEW.equals(intent.getAction())) { 1172 Tab t = getCurrentTab(); 1173 Uri uri = intent.getData(); 1174 loadUrl(t, uri.toString()); 1175 } else if (intent.hasExtra(ComboViewActivity.EXTRA_OPEN_ALL)) { 1176 String[] urls = intent.getStringArrayExtra( 1177 ComboViewActivity.EXTRA_OPEN_ALL); 1178 Tab parent = getCurrentTab(); 1179 for (String url : urls) { 1180 parent = openTab(url, parent, 1181 !mSettings.openInBackground(), true); 1182 } 1183 } else if (intent.hasExtra(ComboViewActivity.EXTRA_OPEN_SNAPSHOT)) { 1184 long id = intent.getLongExtra( 1185 ComboViewActivity.EXTRA_OPEN_SNAPSHOT, -1); 1186 if (id >= 0) { 1187 createNewSnapshotTab(id, true); 1188 } 1189 } 1190 break; 1191 default: 1192 break; 1193 } 1194 getCurrentTopWebView().requestFocus(); 1195 } 1196 1197 /** 1198 * Open the Go page. 1199 * @param startWithHistory If true, open starting on the history tab. 1200 * Otherwise, start with the bookmarks tab. 1201 */ 1202 @Override 1203 public void bookmarksOrHistoryPicker(ComboViews startView) { 1204 if (mTabControl.getCurrentWebView() == null) { 1205 return; 1206 } 1207 // clear action mode 1208 if (isInCustomActionMode()) { 1209 endActionMode(); 1210 } 1211 Bundle extras = new Bundle(); 1212 // Disable opening in a new window if we have maxed out the windows 1213 extras.putBoolean(BrowserBookmarksPage.EXTRA_DISABLE_WINDOW, 1214 !mTabControl.canCreateNewTab()); 1215 mUi.showComboView(startView, extras); 1216 } 1217 1218 // combo view callbacks 1219 1220 // key handling 1221 protected void onBackKey() { 1222 if (!mUi.onBackKey()) { 1223 WebView subwindow = mTabControl.getCurrentSubWindow(); 1224 if (subwindow != null) { 1225 if (subwindow.canGoBack()) { 1226 subwindow.goBack(); 1227 } else { 1228 dismissSubWindow(mTabControl.getCurrentTab()); 1229 } 1230 } else { 1231 goBackOnePageOrQuit(); 1232 } 1233 } 1234 } 1235 1236 protected boolean onMenuKey() { 1237 return mUi.onMenuKey(); 1238 } 1239 1240 // menu handling and state 1241 // TODO: maybe put into separate handler 1242 1243 protected boolean onCreateOptionsMenu(Menu menu) { 1244 if (mMenuState == EMPTY_MENU) { 1245 return false; 1246 } 1247 MenuInflater inflater = mActivity.getMenuInflater(); 1248 inflater.inflate(R.menu.browser, menu); 1249 return true; 1250 } 1251 1252 protected void onCreateContextMenu(ContextMenu menu, View v, 1253 ContextMenuInfo menuInfo) { 1254 if (v instanceof TitleBar) { 1255 return; 1256 } 1257 if (!(v instanceof WebView)) { 1258 return; 1259 } 1260 final WebView webview = (WebView) v; 1261 WebView.HitTestResult result = webview.getHitTestResult(); 1262 if (result == null) { 1263 return; 1264 } 1265 1266 int type = result.getType(); 1267 if (type == WebView.HitTestResult.UNKNOWN_TYPE) { 1268 Log.w(LOGTAG, 1269 "We should not show context menu when nothing is touched"); 1270 return; 1271 } 1272 if (type == WebView.HitTestResult.EDIT_TEXT_TYPE) { 1273 // let TextView handles context menu 1274 return; 1275 } 1276 1277 // Note, http://b/issue?id=1106666 is requesting that 1278 // an inflated menu can be used again. This is not available 1279 // yet, so inflate each time (yuk!) 1280 MenuInflater inflater = mActivity.getMenuInflater(); 1281 inflater.inflate(R.menu.browsercontext, menu); 1282 1283 // Show the correct menu group 1284 final String extra = result.getExtra(); 1285 menu.setGroupVisible(R.id.PHONE_MENU, 1286 type == WebView.HitTestResult.PHONE_TYPE); 1287 menu.setGroupVisible(R.id.EMAIL_MENU, 1288 type == WebView.HitTestResult.EMAIL_TYPE); 1289 menu.setGroupVisible(R.id.GEO_MENU, 1290 type == WebView.HitTestResult.GEO_TYPE); 1291 menu.setGroupVisible(R.id.IMAGE_MENU, 1292 type == WebView.HitTestResult.IMAGE_TYPE 1293 || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE); 1294 menu.setGroupVisible(R.id.ANCHOR_MENU, 1295 type == WebView.HitTestResult.SRC_ANCHOR_TYPE 1296 || type == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE); 1297 boolean hitText = type == WebView.HitTestResult.SRC_ANCHOR_TYPE 1298 || type == WebView.HitTestResult.PHONE_TYPE 1299 || type == WebView.HitTestResult.EMAIL_TYPE 1300 || type == WebView.HitTestResult.GEO_TYPE; 1301 menu.setGroupVisible(R.id.SELECT_TEXT_MENU, hitText); 1302 if (hitText) { 1303 menu.findItem(R.id.select_text_menu_id) 1304 .setOnMenuItemClickListener(new SelectText(webview)); 1305 } 1306 // Setup custom handling depending on the type 1307 switch (type) { 1308 case WebView.HitTestResult.PHONE_TYPE: 1309 menu.setHeaderTitle(Uri.decode(extra)); 1310 menu.findItem(R.id.dial_context_menu_id).setIntent( 1311 new Intent(Intent.ACTION_VIEW, Uri 1312 .parse(WebView.SCHEME_TEL + extra))); 1313 Intent addIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT); 1314 addIntent.putExtra(Insert.PHONE, Uri.decode(extra)); 1315 addIntent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE); 1316 menu.findItem(R.id.add_contact_context_menu_id).setIntent( 1317 addIntent); 1318 menu.findItem(R.id.copy_phone_context_menu_id) 1319 .setOnMenuItemClickListener( 1320 new Copy(extra)); 1321 break; 1322 1323 case WebView.HitTestResult.EMAIL_TYPE: 1324 menu.setHeaderTitle(extra); 1325 menu.findItem(R.id.email_context_menu_id).setIntent( 1326 new Intent(Intent.ACTION_VIEW, Uri 1327 .parse(WebView.SCHEME_MAILTO + extra))); 1328 menu.findItem(R.id.copy_mail_context_menu_id) 1329 .setOnMenuItemClickListener( 1330 new Copy(extra)); 1331 break; 1332 1333 case WebView.HitTestResult.GEO_TYPE: 1334 menu.setHeaderTitle(extra); 1335 menu.findItem(R.id.map_context_menu_id).setIntent( 1336 new Intent(Intent.ACTION_VIEW, Uri 1337 .parse(WebView.SCHEME_GEO 1338 + URLEncoder.encode(extra)))); 1339 menu.findItem(R.id.copy_geo_context_menu_id) 1340 .setOnMenuItemClickListener( 1341 new Copy(extra)); 1342 break; 1343 1344 case WebView.HitTestResult.SRC_ANCHOR_TYPE: 1345 case WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE: 1346 menu.setHeaderTitle(extra); 1347 // decide whether to show the open link in new tab option 1348 boolean showNewTab = mTabControl.canCreateNewTab(); 1349 MenuItem newTabItem 1350 = menu.findItem(R.id.open_newtab_context_menu_id); 1351 newTabItem.setTitle(getSettings().openInBackground() 1352 ? R.string.contextmenu_openlink_newwindow_background 1353 : R.string.contextmenu_openlink_newwindow); 1354 newTabItem.setVisible(showNewTab); 1355 if (showNewTab) { 1356 if (WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE == type) { 1357 newTabItem.setOnMenuItemClickListener( 1358 new MenuItem.OnMenuItemClickListener() { 1359 @Override 1360 public boolean onMenuItemClick(MenuItem item) { 1361 final HashMap<String, WebView> hrefMap = 1362 new HashMap<String, WebView>(); 1363 hrefMap.put("webview", webview); 1364 final Message msg = mHandler.obtainMessage( 1365 FOCUS_NODE_HREF, 1366 R.id.open_newtab_context_menu_id, 1367 0, hrefMap); 1368 webview.requestFocusNodeHref(msg); 1369 return true; 1370 } 1371 }); 1372 } else { 1373 newTabItem.setOnMenuItemClickListener( 1374 new MenuItem.OnMenuItemClickListener() { 1375 @Override 1376 public boolean onMenuItemClick(MenuItem item) { 1377 final Tab parent = mTabControl.getCurrentTab(); 1378 openTab(extra, parent, 1379 !mSettings.openInBackground(), 1380 true); 1381 return true; 1382 } 1383 }); 1384 } 1385 } 1386 if (type == WebView.HitTestResult.SRC_ANCHOR_TYPE) { 1387 break; 1388 } 1389 // otherwise fall through to handle image part 1390 case WebView.HitTestResult.IMAGE_TYPE: 1391 if (type == WebView.HitTestResult.IMAGE_TYPE) { 1392 menu.setHeaderTitle(extra); 1393 } 1394 menu.findItem(R.id.view_image_context_menu_id) 1395 .setOnMenuItemClickListener(new OnMenuItemClickListener() { 1396 @Override 1397 public boolean onMenuItemClick(MenuItem item) { 1398 openTab(extra, mTabControl.getCurrentTab(), true, true); 1399 return false; 1400 } 1401 }); 1402 menu.findItem(R.id.download_context_menu_id). 1403 setOnMenuItemClickListener( 1404 new Download(mActivity, extra, webview.isPrivateBrowsingEnabled())); 1405 menu.findItem(R.id.set_wallpaper_context_menu_id). 1406 setOnMenuItemClickListener(new WallpaperHandler(mActivity, 1407 extra)); 1408 break; 1409 1410 default: 1411 Log.w(LOGTAG, "We should not get here."); 1412 break; 1413 } 1414 //update the ui 1415 mUi.onContextMenuCreated(menu); 1416 } 1417 1418 /** 1419 * As the menu can be open when loading state changes 1420 * we must manually update the state of the stop/reload menu 1421 * item 1422 */ 1423 private void updateInLoadMenuItems(Menu menu, Tab tab) { 1424 if (menu == null) { 1425 return; 1426 } 1427 MenuItem dest = menu.findItem(R.id.stop_reload_menu_id); 1428 MenuItem src = ((tab != null) && tab.inPageLoad()) ? 1429 menu.findItem(R.id.stop_menu_id): 1430 menu.findItem(R.id.reload_menu_id); 1431 if (src != null) { 1432 dest.setIcon(src.getIcon()); 1433 dest.setTitle(src.getTitle()); 1434 } 1435 } 1436 1437 boolean onPrepareOptionsMenu(Menu menu) { 1438 updateInLoadMenuItems(menu, getCurrentTab()); 1439 // hold on to the menu reference here; it is used by the page callbacks 1440 // to update the menu based on loading state 1441 mCachedMenu = menu; 1442 // Note: setVisible will decide whether an item is visible; while 1443 // setEnabled() will decide whether an item is enabled, which also means 1444 // whether the matching shortcut key will function. 1445 switch (mMenuState) { 1446 case EMPTY_MENU: 1447 if (mCurrentMenuState != mMenuState) { 1448 menu.setGroupVisible(R.id.MAIN_MENU, false); 1449 menu.setGroupEnabled(R.id.MAIN_MENU, false); 1450 menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, false); 1451 } 1452 break; 1453 default: 1454 if (mCurrentMenuState != mMenuState) { 1455 menu.setGroupVisible(R.id.MAIN_MENU, true); 1456 menu.setGroupEnabled(R.id.MAIN_MENU, true); 1457 menu.setGroupEnabled(R.id.MAIN_SHORTCUT_MENU, true); 1458 } 1459 updateMenuState(getCurrentTab(), menu); 1460 break; 1461 } 1462 mCurrentMenuState = mMenuState; 1463 return mUi.onPrepareOptionsMenu(menu); 1464 } 1465 1466 @Override 1467 public void updateMenuState(Tab tab, Menu menu) { 1468 boolean canGoBack = false; 1469 boolean canGoForward = false; 1470 boolean isHome = false; 1471 boolean isDesktopUa = false; 1472 boolean isLive = false; 1473 if (tab != null) { 1474 canGoBack = tab.canGoBack(); 1475 canGoForward = tab.canGoForward(); 1476 isHome = mSettings.getHomePage().equals(tab.getUrl()); 1477 isDesktopUa = mSettings.hasDesktopUseragent(tab.getWebView()); 1478 isLive = !tab.isSnapshot(); 1479 } 1480 final MenuItem back = menu.findItem(R.id.back_menu_id); 1481 back.setEnabled(canGoBack); 1482 1483 final MenuItem home = menu.findItem(R.id.homepage_menu_id); 1484 home.setEnabled(!isHome); 1485 1486 final MenuItem forward = menu.findItem(R.id.forward_menu_id); 1487 forward.setEnabled(canGoForward); 1488 1489 final MenuItem source = menu.findItem(isInLoad() ? R.id.stop_menu_id 1490 : R.id.reload_menu_id); 1491 final MenuItem dest = menu.findItem(R.id.stop_reload_menu_id); 1492 if (source != null && dest != null) { 1493 dest.setTitle(source.getTitle()); 1494 dest.setIcon(source.getIcon()); 1495 } 1496 menu.setGroupVisible(R.id.NAV_MENU, isLive); 1497 1498 // decide whether to show the share link option 1499 PackageManager pm = mActivity.getPackageManager(); 1500 Intent send = new Intent(Intent.ACTION_SEND); 1501 send.setType("text/plain"); 1502 ResolveInfo ri = pm.resolveActivity(send, 1503 PackageManager.MATCH_DEFAULT_ONLY); 1504 menu.findItem(R.id.share_page_menu_id).setVisible(ri != null); 1505 1506 boolean isNavDump = mSettings.enableNavDump(); 1507 final MenuItem nav = menu.findItem(R.id.dump_nav_menu_id); 1508 nav.setVisible(isNavDump); 1509 nav.setEnabled(isNavDump); 1510 1511 boolean showDebugSettings = mSettings.isDebugEnabled(); 1512 final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id); 1513 counter.setVisible(showDebugSettings); 1514 counter.setEnabled(showDebugSettings); 1515 final MenuItem uaSwitcher = menu.findItem(R.id.ua_desktop_menu_id); 1516 uaSwitcher.setChecked(isDesktopUa); 1517 menu.setGroupVisible(R.id.LIVE_MENU, isLive); 1518 menu.setGroupVisible(R.id.SNAPSHOT_MENU, !isLive); 1519 menu.setGroupVisible(R.id.COMBO_MENU, false); 1520 1521 mUi.updateMenuState(tab, menu); 1522 } 1523 1524 public boolean onOptionsItemSelected(MenuItem item) { 1525 if (null == getCurrentTopWebView()) { 1526 return false; 1527 } 1528 if (mMenuIsDown) { 1529 // The shortcut action consumes the MENU. Even if it is still down, 1530 // it won't trigger the next shortcut action. In the case of the 1531 // shortcut action triggering a new activity, like Bookmarks, we 1532 // won't get onKeyUp for MENU. So it is important to reset it here. 1533 mMenuIsDown = false; 1534 } 1535 if (mUi.onOptionsItemSelected(item)) { 1536 // ui callback handled it 1537 return true; 1538 } 1539 switch (item.getItemId()) { 1540 // -- Main menu 1541 case R.id.new_tab_menu_id: 1542 openTabToHomePage(); 1543 break; 1544 1545 case R.id.incognito_menu_id: 1546 openIncognitoTab(); 1547 break; 1548 1549 case R.id.goto_menu_id: 1550 editUrl(); 1551 break; 1552 1553 case R.id.bookmarks_menu_id: 1554 bookmarksOrHistoryPicker(ComboViews.Bookmarks); 1555 break; 1556 1557 case R.id.history_menu_id: 1558 bookmarksOrHistoryPicker(ComboViews.History); 1559 break; 1560 1561 case R.id.snapshots_menu_id: 1562 bookmarksOrHistoryPicker(ComboViews.Snapshots); 1563 break; 1564 1565 case R.id.add_bookmark_menu_id: 1566 Intent bookmarkIntent = createBookmarkCurrentPageIntent(false); 1567 if (bookmarkIntent != null) { 1568 mActivity.startActivity(bookmarkIntent); 1569 } 1570 break; 1571 1572 case R.id.stop_reload_menu_id: 1573 if (isInLoad()) { 1574 stopLoading(); 1575 } else { 1576 getCurrentTopWebView().reload(); 1577 } 1578 break; 1579 1580 case R.id.back_menu_id: 1581 getCurrentTab().goBack(); 1582 break; 1583 1584 case R.id.forward_menu_id: 1585 getCurrentTab().goForward(); 1586 break; 1587 1588 case R.id.close_menu_id: 1589 // Close the subwindow if it exists. 1590 if (mTabControl.getCurrentSubWindow() != null) { 1591 dismissSubWindow(mTabControl.getCurrentTab()); 1592 break; 1593 } 1594 closeCurrentTab(); 1595 break; 1596 1597 case R.id.homepage_menu_id: 1598 Tab current = mTabControl.getCurrentTab(); 1599 loadUrl(current, mSettings.getHomePage()); 1600 break; 1601 1602 case R.id.preferences_menu_id: 1603 Intent intent = new Intent(mActivity, BrowserPreferencesPage.class); 1604 intent.putExtra(BrowserPreferencesPage.CURRENT_PAGE, 1605 getCurrentTopWebView().getUrl()); 1606 mActivity.startActivityForResult(intent, PREFERENCES_PAGE); 1607 break; 1608 1609 case R.id.find_menu_id: 1610 getCurrentTopWebView().showFindDialog(null, true); 1611 break; 1612 1613 case R.id.save_snapshot_menu_id: 1614 final Tab source = getTabControl().getCurrentTab(); 1615 if (source == null) break; 1616 final ContentResolver cr = mActivity.getContentResolver(); 1617 final ContentValues values = source.createSnapshotValues(); 1618 if (values != null) { 1619 new AsyncTask<Tab, Void, Long>() { 1620 1621 @Override 1622 protected Long doInBackground(Tab... params) { 1623 Uri result = cr.insert(Snapshots.CONTENT_URI, values); 1624 if (result == null) { 1625 return null; 1626 } 1627 long id = ContentUris.parseId(result); 1628 return id; 1629 } 1630 1631 @Override 1632 protected void onPostExecute(Long id) { 1633 if (id == null) { 1634 Toast.makeText(mActivity, R.string.snapshot_failed, 1635 Toast.LENGTH_SHORT).show(); 1636 return; 1637 } 1638 Bundle b = new Bundle(); 1639 b.putLong(BrowserSnapshotPage.EXTRA_ANIMATE_ID, id); 1640 mUi.showComboView(ComboViews.Snapshots, b); 1641 }; 1642 }.execute(source); 1643 } else { 1644 Toast.makeText(mActivity, R.string.snapshot_failed, 1645 Toast.LENGTH_SHORT).show(); 1646 } 1647 break; 1648 1649 case R.id.page_info_menu_id: 1650 showPageInfo(); 1651 break; 1652 1653 case R.id.snapshot_go_live: 1654 goLive(); 1655 return true; 1656 1657 case R.id.share_page_menu_id: 1658 Tab currentTab = mTabControl.getCurrentTab(); 1659 if (null == currentTab) { 1660 return false; 1661 } 1662 shareCurrentPage(currentTab); 1663 break; 1664 1665 case R.id.dump_nav_menu_id: 1666 getCurrentTopWebView().debugDump(); 1667 break; 1668 1669 case R.id.dump_counters_menu_id: 1670 getCurrentTopWebView().dumpV8Counters(); 1671 break; 1672 1673 case R.id.zoom_in_menu_id: 1674 getCurrentTopWebView().zoomIn(); 1675 break; 1676 1677 case R.id.zoom_out_menu_id: 1678 getCurrentTopWebView().zoomOut(); 1679 break; 1680 1681 case R.id.view_downloads_menu_id: 1682 viewDownloads(); 1683 break; 1684 1685 case R.id.ua_desktop_menu_id: 1686 WebView web = getCurrentWebView(); 1687 mSettings.toggleDesktopUseragent(web); 1688 web.loadUrl(web.getOriginalUrl()); 1689 break; 1690 1691 case R.id.window_one_menu_id: 1692 case R.id.window_two_menu_id: 1693 case R.id.window_three_menu_id: 1694 case R.id.window_four_menu_id: 1695 case R.id.window_five_menu_id: 1696 case R.id.window_six_menu_id: 1697 case R.id.window_seven_menu_id: 1698 case R.id.window_eight_menu_id: 1699 { 1700 int menuid = item.getItemId(); 1701 for (int id = 0; id < WINDOW_SHORTCUT_ID_ARRAY.length; id++) { 1702 if (WINDOW_SHORTCUT_ID_ARRAY[id] == menuid) { 1703 Tab desiredTab = mTabControl.getTab(id); 1704 if (desiredTab != null && 1705 desiredTab != mTabControl.getCurrentTab()) { 1706 switchToTab(desiredTab); 1707 } 1708 break; 1709 } 1710 } 1711 } 1712 break; 1713 1714 default: 1715 return false; 1716 } 1717 return true; 1718 } 1719 1720 private void goLive() { 1721 Tab t = getCurrentTab(); 1722 t.loadUrl(t.getUrl(), null); 1723 } 1724 1725 @Override 1726 public void showPageInfo() { 1727 mPageDialogsHandler.showPageInfo(mTabControl.getCurrentTab(), false, null); 1728 } 1729 1730 public boolean onContextItemSelected(MenuItem item) { 1731 // Let the History and Bookmark fragments handle menus they created. 1732 if (item.getGroupId() == R.id.CONTEXT_MENU) { 1733 return false; 1734 } 1735 1736 int id = item.getItemId(); 1737 boolean result = true; 1738 switch (id) { 1739 // -- Browser context menu 1740 case R.id.open_context_menu_id: 1741 case R.id.save_link_context_menu_id: 1742 case R.id.copy_link_context_menu_id: 1743 final WebView webView = getCurrentTopWebView(); 1744 if (null == webView) { 1745 result = false; 1746 break; 1747 } 1748 final HashMap<String, WebView> hrefMap = 1749 new HashMap<String, WebView>(); 1750 hrefMap.put("webview", webView); 1751 final Message msg = mHandler.obtainMessage( 1752 FOCUS_NODE_HREF, id, 0, hrefMap); 1753 webView.requestFocusNodeHref(msg); 1754 break; 1755 1756 default: 1757 // For other context menus 1758 result = onOptionsItemSelected(item); 1759 } 1760 return result; 1761 } 1762 1763 /** 1764 * support programmatically opening the context menu 1765 */ 1766 public void openContextMenu(View view) { 1767 mActivity.openContextMenu(view); 1768 } 1769 1770 /** 1771 * programmatically open the options menu 1772 */ 1773 public void openOptionsMenu() { 1774 mActivity.openOptionsMenu(); 1775 } 1776 1777 public boolean onMenuOpened(int featureId, Menu menu) { 1778 if (mOptionsMenuOpen) { 1779 if (mConfigChanged) { 1780 // We do not need to make any changes to the state of the 1781 // title bar, since the only thing that happened was a 1782 // change in orientation 1783 mConfigChanged = false; 1784 } else { 1785 if (!mExtendedMenuOpen) { 1786 mExtendedMenuOpen = true; 1787 mUi.onExtendedMenuOpened(); 1788 } else { 1789 // Switching the menu back to icon view, so show the 1790 // title bar once again. 1791 mExtendedMenuOpen = false; 1792 mUi.onExtendedMenuClosed(isInLoad()); 1793 } 1794 } 1795 } else { 1796 // The options menu is closed, so open it, and show the title 1797 mOptionsMenuOpen = true; 1798 mConfigChanged = false; 1799 mExtendedMenuOpen = false; 1800 mUi.onOptionsMenuOpened(); 1801 } 1802 return true; 1803 } 1804 1805 public void onOptionsMenuClosed(Menu menu) { 1806 mOptionsMenuOpen = false; 1807 mUi.onOptionsMenuClosed(isInLoad()); 1808 } 1809 1810 public void onContextMenuClosed(Menu menu) { 1811 mUi.onContextMenuClosed(menu, isInLoad()); 1812 } 1813 1814 // Helper method for getting the top window. 1815 @Override 1816 public WebView getCurrentTopWebView() { 1817 return mTabControl.getCurrentTopWebView(); 1818 } 1819 1820 @Override 1821 public WebView getCurrentWebView() { 1822 return mTabControl.getCurrentWebView(); 1823 } 1824 1825 /* 1826 * This method is called as a result of the user selecting the options 1827 * menu to see the download window. It shows the download window on top of 1828 * the current window. 1829 */ 1830 void viewDownloads() { 1831 Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS); 1832 mActivity.startActivity(intent); 1833 } 1834 1835 int getActionModeHeight() { 1836 TypedArray actionBarSizeTypedArray = mActivity.obtainStyledAttributes( 1837 new int[] { android.R.attr.actionBarSize }); 1838 int size = (int) actionBarSizeTypedArray.getDimension(0, 0f); 1839 actionBarSizeTypedArray.recycle(); 1840 return size; 1841 } 1842 1843 // action mode 1844 1845 void onActionModeStarted(ActionMode mode) { 1846 mUi.onActionModeStarted(mode); 1847 mActionMode = mode; 1848 } 1849 1850 /* 1851 * True if a custom ActionMode (i.e. find or select) is in use. 1852 */ 1853 @Override 1854 public boolean isInCustomActionMode() { 1855 return mActionMode != null; 1856 } 1857 1858 /* 1859 * End the current ActionMode. 1860 */ 1861 @Override 1862 public void endActionMode() { 1863 if (mActionMode != null) { 1864 mActionMode.finish(); 1865 } 1866 } 1867 1868 /* 1869 * Called by find and select when they are finished. Replace title bars 1870 * as necessary. 1871 */ 1872 public void onActionModeFinished(ActionMode mode) { 1873 if (!isInCustomActionMode()) return; 1874 mUi.onActionModeFinished(isInLoad()); 1875 mActionMode = null; 1876 } 1877 1878 boolean isInLoad() { 1879 final Tab tab = getCurrentTab(); 1880 return (tab != null) && tab.inPageLoad(); 1881 } 1882 1883 // bookmark handling 1884 1885 /** 1886 * add the current page as a bookmark to the given folder id 1887 * @param folderId use -1 for the default folder 1888 * @param editExisting If true, check to see whether the site is already 1889 * bookmarked, and if it is, edit that bookmark. If false, and 1890 * the site is already bookmarked, do not attempt to edit the 1891 * existing bookmark. 1892 */ 1893 @Override 1894 public Intent createBookmarkCurrentPageIntent(boolean editExisting) { 1895 WebView w = getCurrentTopWebView(); 1896 if (w == null) { 1897 return null; 1898 } 1899 Intent i = new Intent(mActivity, 1900 AddBookmarkPage.class); 1901 i.putExtra(BrowserContract.Bookmarks.URL, w.getUrl()); 1902 i.putExtra(BrowserContract.Bookmarks.TITLE, w.getTitle()); 1903 String touchIconUrl = w.getTouchIconUrl(); 1904 if (touchIconUrl != null) { 1905 i.putExtra(AddBookmarkPage.TOUCH_ICON_URL, touchIconUrl); 1906 WebSettings settings = w.getSettings(); 1907 if (settings != null) { 1908 i.putExtra(AddBookmarkPage.USER_AGENT, 1909 settings.getUserAgentString()); 1910 } 1911 } 1912 i.putExtra(BrowserContract.Bookmarks.THUMBNAIL, 1913 createScreenshot(w, getDesiredThumbnailWidth(mActivity), 1914 getDesiredThumbnailHeight(mActivity))); 1915 i.putExtra(BrowserContract.Bookmarks.FAVICON, w.getFavicon()); 1916 if (editExisting) { 1917 i.putExtra(AddBookmarkPage.CHECK_FOR_DUPE, true); 1918 } 1919 // Put the dialog at the upper right of the screen, covering the 1920 // star on the title bar. 1921 i.putExtra("gravity", Gravity.RIGHT | Gravity.TOP); 1922 return i; 1923 } 1924 1925 // file chooser 1926 public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) { 1927 mUploadHandler = new UploadHandler(this); 1928 mUploadHandler.openFileChooser(uploadMsg, acceptType); 1929 } 1930 1931 // thumbnails 1932 1933 /** 1934 * Return the desired width for thumbnail screenshots, which are stored in 1935 * the database, and used on the bookmarks screen. 1936 * @param context Context for finding out the density of the screen. 1937 * @return desired width for thumbnail screenshot. 1938 */ 1939 static int getDesiredThumbnailWidth(Context context) { 1940 return context.getResources().getDimensionPixelOffset( 1941 R.dimen.bookmarkThumbnailWidth); 1942 } 1943 1944 /** 1945 * Return the desired height for thumbnail screenshots, which are stored in 1946 * the database, and used on the bookmarks screen. 1947 * @param context Context for finding out the density of the screen. 1948 * @return desired height for thumbnail screenshot. 1949 */ 1950 static int getDesiredThumbnailHeight(Context context) { 1951 return context.getResources().getDimensionPixelOffset( 1952 R.dimen.bookmarkThumbnailHeight); 1953 } 1954 1955 static Bitmap createScreenshot(WebView view, int width, int height) { 1956 if (view == null || view.getContentHeight() == 0 1957 || view.getContentWidth() == 0) { 1958 return null; 1959 } 1960 // We render to a bitmap 2x the desired size so that we can then 1961 // re-scale it with filtering since canvas.scale doesn't filter 1962 // This helps reduce aliasing at the cost of being slightly blurry 1963 final int filter_scale = 2; 1964 int scaledWidth = width * filter_scale; 1965 int scaledHeight = height * filter_scale; 1966 if (sThumbnailBitmap == null || sThumbnailBitmap.getWidth() != scaledWidth 1967 || sThumbnailBitmap.getHeight() != scaledHeight) { 1968 if (sThumbnailBitmap != null) { 1969 sThumbnailBitmap.recycle(); 1970 sThumbnailBitmap = null; 1971 } 1972 sThumbnailBitmap = 1973 Bitmap.createBitmap(scaledWidth, scaledHeight, Bitmap.Config.RGB_565); 1974 } 1975 Canvas canvas = new Canvas(sThumbnailBitmap); 1976 int contentWidth = view.getContentWidth(); 1977 float overviewScale = scaledWidth / (view.getScale() * contentWidth); 1978 if (view instanceof BrowserWebView) { 1979 int dy = -((BrowserWebView)view).getTitleHeight(); 1980 canvas.translate(0, dy * overviewScale); 1981 } 1982 1983 canvas.scale(overviewScale, overviewScale); 1984 1985 if (view instanceof BrowserWebView) { 1986 ((BrowserWebView)view).drawContent(canvas); 1987 } else { 1988 view.draw(canvas); 1989 } 1990 Bitmap ret = Bitmap.createScaledBitmap(sThumbnailBitmap, 1991 width, height, true); 1992 canvas.setBitmap(null); 1993 return ret; 1994 } 1995 1996 private void updateScreenshot(Tab tab) { 1997 // If this is a bookmarked site, add a screenshot to the database. 1998 // FIXME: Would like to make sure there is actually something to 1999 // draw, but the API for that (WebViewCore.pictureReady()) is not 2000 // currently accessible here. 2001 2002 WebView view = tab.getWebView(); 2003 if (view == null) { 2004 // Tab was destroyed 2005 return; 2006 } 2007 final String url = tab.getUrl(); 2008 final String originalUrl = view.getOriginalUrl(); 2009 2010 if (TextUtils.isEmpty(url)) { 2011 return; 2012 } 2013 2014 // Only update thumbnails for web urls (http(s)://), not for 2015 // about:, javascript:, data:, etc... 2016 // Unless it is a bookmarked site, then always update 2017 if (!Patterns.WEB_URL.matcher(url).matches() && !tab.isBookmarkedSite()) { 2018 return; 2019 } 2020 2021 final Bitmap bm = createScreenshot(view, getDesiredThumbnailWidth(mActivity), 2022 getDesiredThumbnailHeight(mActivity)); 2023 if (bm == null) { 2024 return; 2025 } 2026 2027 final ContentResolver cr = mActivity.getContentResolver(); 2028 new AsyncTask<Void, Void, Void>() { 2029 @Override 2030 protected Void doInBackground(Void... unused) { 2031 Cursor cursor = null; 2032 try { 2033 // TODO: Clean this up 2034 cursor = Bookmarks.queryCombinedForUrl(cr, originalUrl, url); 2035 if (cursor != null && cursor.moveToFirst()) { 2036 final ByteArrayOutputStream os = 2037 new ByteArrayOutputStream(); 2038 bm.compress(Bitmap.CompressFormat.PNG, 100, os); 2039 2040 ContentValues values = new ContentValues(); 2041 values.put(Images.THUMBNAIL, os.toByteArray()); 2042 2043 do { 2044 values.put(Images.URL, cursor.getString(0)); 2045 cr.update(Images.CONTENT_URI, values, null, null); 2046 } while (cursor.moveToNext()); 2047 } 2048 } catch (IllegalStateException e) { 2049 // Ignore 2050 } finally { 2051 if (cursor != null) cursor.close(); 2052 } 2053 return null; 2054 } 2055 }.execute(); 2056 } 2057 2058 private class Copy implements OnMenuItemClickListener { 2059 private CharSequence mText; 2060 2061 public boolean onMenuItemClick(MenuItem item) { 2062 copy(mText); 2063 return true; 2064 } 2065 2066 public Copy(CharSequence toCopy) { 2067 mText = toCopy; 2068 } 2069 } 2070 2071 private static class Download implements OnMenuItemClickListener { 2072 private Activity mActivity; 2073 private String mText; 2074 private boolean mPrivateBrowsing; 2075 private static final String FALLBACK_EXTENSION = "dat"; 2076 private static final String IMAGE_BASE_FORMAT = "yyyy-MM-dd-HH-mm-ss-"; 2077 2078 public boolean onMenuItemClick(MenuItem item) { 2079 if (DataUri.isDataUri(mText)) { 2080 saveDataUri(); 2081 } else { 2082 DownloadHandler.onDownloadStartNoStream(mActivity, mText, null, 2083 null, null, mPrivateBrowsing); 2084 } 2085 return true; 2086 } 2087 2088 public Download(Activity activity, String toDownload, boolean privateBrowsing) { 2089 mActivity = activity; 2090 mText = toDownload; 2091 mPrivateBrowsing = privateBrowsing; 2092 } 2093 2094 /** 2095 * Treats mText as a data URI and writes its contents to a file 2096 * based on the current time. 2097 */ 2098 private void saveDataUri() { 2099 FileOutputStream outputStream = null; 2100 try { 2101 DataUri uri = new DataUri(mText); 2102 File target = getTarget(uri); 2103 outputStream = new FileOutputStream(target); 2104 outputStream.write(uri.getData()); 2105 final DownloadManager manager = 2106 (DownloadManager) mActivity.getSystemService(Context.DOWNLOAD_SERVICE); 2107 manager.addCompletedDownload(target.getName(), 2108 mActivity.getTitle().toString(), false, 2109 uri.getMimeType(), target.getAbsolutePath(), 2110 (long)uri.getData().length, false); 2111 } catch (IOException e) { 2112 Log.e(LOGTAG, "Could not save data URL"); 2113 } finally { 2114 if (outputStream != null) { 2115 try { 2116 outputStream.close(); 2117 } catch (IOException e) { 2118 // ignore close errors 2119 } 2120 } 2121 } 2122 } 2123 2124 /** 2125 * Creates a File based on the current time stamp and uses 2126 * the mime type of the DataUri to get the extension. 2127 */ 2128 private File getTarget(DataUri uri) throws IOException { 2129 File dir = mActivity.getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS); 2130 DateFormat format = new SimpleDateFormat(IMAGE_BASE_FORMAT); 2131 String nameBase = format.format(new Date()); 2132 String mimeType = uri.getMimeType(); 2133 MimeTypeMap mimeTypeMap = MimeTypeMap.getSingleton(); 2134 String extension = mimeTypeMap.getExtensionFromMimeType(mimeType); 2135 if (extension == null) { 2136 Log.w(LOGTAG, "Unknown mime type in data URI" + mimeType); 2137 extension = FALLBACK_EXTENSION; 2138 } 2139 extension = "." + extension; // createTempFile needs the '.' 2140 File targetFile = File.createTempFile(nameBase, extension, dir); 2141 return targetFile; 2142 } 2143 } 2144 2145 private static class SelectText implements OnMenuItemClickListener { 2146 private WebView mWebView; 2147 2148 public boolean onMenuItemClick(MenuItem item) { 2149 if (mWebView != null) { 2150 return mWebView.selectText(); 2151 } 2152 return false; 2153 } 2154 2155 public SelectText(WebView webView) { 2156 mWebView = webView; 2157 } 2158 2159 } 2160 2161 /********************** TODO: UI stuff *****************************/ 2162 2163 // these methods have been copied, they still need to be cleaned up 2164 2165 /****************** tabs ***************************************************/ 2166 2167 // basic tab interactions: 2168 2169 // it is assumed that tabcontrol already knows about the tab 2170 protected void addTab(Tab tab) { 2171 mUi.addTab(tab); 2172 } 2173 2174 protected void removeTab(Tab tab) { 2175 mUi.removeTab(tab); 2176 mTabControl.removeTab(tab); 2177 mCrashRecoveryHandler.backupState(); 2178 } 2179 2180 @Override 2181 public void setActiveTab(Tab tab) { 2182 // monkey protection against delayed start 2183 if (tab != null) { 2184 mTabControl.setCurrentTab(tab); 2185 // the tab is guaranteed to have a webview after setCurrentTab 2186 mUi.setActiveTab(tab); 2187 } 2188 } 2189 2190 protected void closeEmptyTab() { 2191 Tab current = mTabControl.getCurrentTab(); 2192 if (current != null 2193 && current.getWebView().copyBackForwardList().getSize() == 0) { 2194 closeCurrentTab(); 2195 } 2196 } 2197 2198 protected void reuseTab(Tab appTab, UrlData urlData) { 2199 // Dismiss the subwindow if applicable. 2200 dismissSubWindow(appTab); 2201 // Since we might kill the WebView, remove it from the 2202 // content view first. 2203 mUi.detachTab(appTab); 2204 // Recreate the main WebView after destroying the old one. 2205 mTabControl.recreateWebView(appTab); 2206 // TODO: analyze why the remove and add are necessary 2207 mUi.attachTab(appTab); 2208 if (mTabControl.getCurrentTab() != appTab) { 2209 switchToTab(appTab); 2210 loadUrlDataIn(appTab, urlData); 2211 } else { 2212 // If the tab was the current tab, we have to attach 2213 // it to the view system again. 2214 setActiveTab(appTab); 2215 loadUrlDataIn(appTab, urlData); 2216 } 2217 } 2218 2219 // Remove the sub window if it exists. Also called by TabControl when the 2220 // user clicks the 'X' to dismiss a sub window. 2221 public void dismissSubWindow(Tab tab) { 2222 removeSubWindow(tab); 2223 // dismiss the subwindow. This will destroy the WebView. 2224 tab.dismissSubWindow(); 2225 WebView wv = getCurrentTopWebView(); 2226 if (wv != null) { 2227 wv.requestFocus(); 2228 } 2229 } 2230 2231 @Override 2232 public void removeSubWindow(Tab t) { 2233 if (t.getSubWebView() != null) { 2234 mUi.removeSubWindow(t.getSubViewContainer()); 2235 } 2236 } 2237 2238 @Override 2239 public void attachSubWindow(Tab tab) { 2240 if (tab.getSubWebView() != null) { 2241 mUi.attachSubWindow(tab.getSubViewContainer()); 2242 getCurrentTopWebView().requestFocus(); 2243 } 2244 } 2245 2246 private Tab showPreloadedTab(final UrlData urlData) { 2247 if (!urlData.isPreloaded()) { 2248 return null; 2249 } 2250 final PreloadedTabControl tabControl = urlData.getPreloadedTab(); 2251 final String sbQuery = urlData.getSearchBoxQueryToSubmit(); 2252 if (sbQuery != null) { 2253 if (!tabControl.searchBoxSubmit(sbQuery, urlData.mUrl, urlData.mHeaders)) { 2254 // Could not submit query. Fallback to regular tab creation 2255 tabControl.destroy(); 2256 return null; 2257 } 2258 } 2259 // check tab count and make room for new tab 2260 if (!mTabControl.canCreateNewTab()) { 2261 Tab leastUsed = mTabControl.getLeastUsedTab(getCurrentTab()); 2262 if (leastUsed != null) { 2263 closeTab(leastUsed); 2264 } 2265 } 2266 Tab t = tabControl.getTab(); 2267 t.refreshIdAfterPreload(); 2268 mTabControl.addPreloadedTab(t); 2269 addTab(t); 2270 setActiveTab(t); 2271 return t; 2272 } 2273 2274 // open a non inconito tab with the given url data 2275 // and set as active tab 2276 public Tab openTab(UrlData urlData) { 2277 Tab tab = showPreloadedTab(urlData); 2278 if (tab == null) { 2279 tab = createNewTab(false, true, true); 2280 if ((tab != null) && !urlData.isEmpty()) { 2281 loadUrlDataIn(tab, urlData); 2282 } 2283 } 2284 return tab; 2285 } 2286 2287 @Override 2288 public Tab openTabToHomePage() { 2289 return openTab(mSettings.getHomePage(), false, true, false); 2290 } 2291 2292 @Override 2293 public Tab openIncognitoTab() { 2294 return openTab(INCOGNITO_URI, true, true, false); 2295 } 2296 2297 @Override 2298 public Tab openTab(String url, boolean incognito, boolean setActive, 2299 boolean useCurrent) { 2300 return openTab(url, incognito, setActive, useCurrent, null); 2301 } 2302 2303 @Override 2304 public Tab openTab(String url, Tab parent, boolean setActive, 2305 boolean useCurrent) { 2306 return openTab(url, (parent != null) && parent.isPrivateBrowsingEnabled(), 2307 setActive, useCurrent, parent); 2308 } 2309 2310 public Tab openTab(String url, boolean incognito, boolean setActive, 2311 boolean useCurrent, Tab parent) { 2312 Tab tab = createNewTab(incognito, setActive, useCurrent); 2313 if (tab != null) { 2314 if (parent != null && parent != tab) { 2315 parent.addChildTab(tab); 2316 } 2317 if (url != null) { 2318 loadUrl(tab, url); 2319 } 2320 } 2321 return tab; 2322 } 2323 2324 // this method will attempt to create a new tab 2325 // incognito: private browsing tab 2326 // setActive: ste tab as current tab 2327 // useCurrent: if no new tab can be created, return current tab 2328 private Tab createNewTab(boolean incognito, boolean setActive, 2329 boolean useCurrent) { 2330 Tab tab = null; 2331 if (mTabControl.canCreateNewTab()) { 2332 tab = mTabControl.createNewTab(incognito); 2333 addTab(tab); 2334 if (setActive) { 2335 setActiveTab(tab); 2336 } 2337 } else { 2338 if (useCurrent) { 2339 tab = mTabControl.getCurrentTab(); 2340 reuseTab(tab, null); 2341 } else { 2342 mUi.showMaxTabsWarning(); 2343 } 2344 } 2345 return tab; 2346 } 2347 2348 @Override 2349 public SnapshotTab createNewSnapshotTab(long snapshotId, boolean setActive) { 2350 SnapshotTab tab = null; 2351 if (mTabControl.canCreateNewTab()) { 2352 tab = mTabControl.createSnapshotTab(snapshotId); 2353 addTab(tab); 2354 if (setActive) { 2355 setActiveTab(tab); 2356 } 2357 } else { 2358 mUi.showMaxTabsWarning(); 2359 } 2360 return tab; 2361 } 2362 2363 /** 2364 * @param tab the tab to switch to 2365 * @return boolean True if we successfully switched to a different tab. If 2366 * the indexth tab is null, or if that tab is the same as 2367 * the current one, return false. 2368 */ 2369 @Override 2370 public boolean switchToTab(Tab tab) { 2371 Tab currentTab = mTabControl.getCurrentTab(); 2372 if (tab == null || tab == currentTab) { 2373 return false; 2374 } 2375 setActiveTab(tab); 2376 return true; 2377 } 2378 2379 @Override 2380 public void closeCurrentTab() { 2381 closeCurrentTab(false); 2382 } 2383 2384 protected void closeCurrentTab(boolean andQuit) { 2385 if (mTabControl.getTabCount() == 1) { 2386 mCrashRecoveryHandler.clearState(); 2387 mTabControl.removeTab(getCurrentTab()); 2388 mActivity.finish(); 2389 return; 2390 } 2391 final Tab current = mTabControl.getCurrentTab(); 2392 final int pos = mTabControl.getCurrentPosition(); 2393 Tab newTab = current.getParent(); 2394 if (newTab == null) { 2395 newTab = mTabControl.getTab(pos + 1); 2396 if (newTab == null) { 2397 newTab = mTabControl.getTab(pos - 1); 2398 } 2399 } 2400 if (andQuit) { 2401 mTabControl.setCurrentTab(newTab); 2402 closeTab(current); 2403 } else if (switchToTab(newTab)) { 2404 // Close window 2405 closeTab(current); 2406 } 2407 } 2408 2409 /** 2410 * Close the tab, remove its associated title bar, and adjust mTabControl's 2411 * current tab to a valid value. 2412 */ 2413 @Override 2414 public void closeTab(Tab tab) { 2415 if (tab == mTabControl.getCurrentTab()) { 2416 closeCurrentTab(); 2417 } else { 2418 removeTab(tab); 2419 } 2420 } 2421 2422 // Called when loading from context menu or LOAD_URL message 2423 protected void loadUrlFromContext(String url) { 2424 Tab tab = getCurrentTab(); 2425 WebView view = tab != null ? tab.getWebView() : null; 2426 // In case the user enters nothing. 2427 if (url != null && url.length() != 0 && tab != null && view != null) { 2428 url = UrlUtils.smartUrlFilter(url); 2429 if (!view.getWebViewClient().shouldOverrideUrlLoading(view, url)) { 2430 loadUrl(tab, url); 2431 } 2432 } 2433 } 2434 2435 /** 2436 * Load the URL into the given WebView and update the title bar 2437 * to reflect the new load. Call this instead of WebView.loadUrl 2438 * directly. 2439 * @param view The WebView used to load url. 2440 * @param url The URL to load. 2441 */ 2442 @Override 2443 public void loadUrl(Tab tab, String url) { 2444 loadUrl(tab, url, null); 2445 } 2446 2447 protected void loadUrl(Tab tab, String url, Map<String, String> headers) { 2448 if (tab != null) { 2449 dismissSubWindow(tab); 2450 tab.loadUrl(url, headers); 2451 mUi.onProgressChanged(tab); 2452 } 2453 } 2454 2455 /** 2456 * Load UrlData into a Tab and update the title bar to reflect the new 2457 * load. Call this instead of UrlData.loadIn directly. 2458 * @param t The Tab used to load. 2459 * @param data The UrlData being loaded. 2460 */ 2461 protected void loadUrlDataIn(Tab t, UrlData data) { 2462 if (data != null) { 2463 if (data.mVoiceIntent != null) { 2464 t.activateVoiceSearchMode(data.mVoiceIntent); 2465 } else if (data.isPreloaded()) { 2466 // this isn't called for preloaded tabs 2467 } else { 2468 loadUrl(t, data.mUrl, data.mHeaders); 2469 } 2470 } 2471 } 2472 2473 @Override 2474 public void onUserCanceledSsl(Tab tab) { 2475 // TODO: Figure out the "right" behavior 2476 if (tab.canGoBack()) { 2477 tab.goBack(); 2478 } else { 2479 tab.loadUrl(mSettings.getHomePage(), null); 2480 } 2481 } 2482 2483 void goBackOnePageOrQuit() { 2484 Tab current = mTabControl.getCurrentTab(); 2485 if (current == null) { 2486 /* 2487 * Instead of finishing the activity, simply push this to the back 2488 * of the stack and let ActivityManager to choose the foreground 2489 * activity. As BrowserActivity is singleTask, it will be always the 2490 * root of the task. So we can use either true or false for 2491 * moveTaskToBack(). 2492 */ 2493 mActivity.moveTaskToBack(true); 2494 return; 2495 } 2496 if (current.canGoBack()) { 2497 current.goBack(); 2498 } else { 2499 // Check to see if we are closing a window that was created by 2500 // another window. If so, we switch back to that window. 2501 Tab parent = current.getParent(); 2502 if (parent != null) { 2503 switchToTab(parent); 2504 // Now we close the other tab 2505 closeTab(current); 2506 } else { 2507 if ((current.getAppId() != null) || current.closeOnBack()) { 2508 closeCurrentTab(true); 2509 } 2510 /* 2511 * Instead of finishing the activity, simply push this to the back 2512 * of the stack and let ActivityManager to choose the foreground 2513 * activity. As BrowserActivity is singleTask, it will be always the 2514 * root of the task. So we can use either true or false for 2515 * moveTaskToBack(). 2516 */ 2517 mActivity.moveTaskToBack(true); 2518 } 2519 } 2520 } 2521 2522 /** 2523 * Feed the previously stored results strings to the BrowserProvider so that 2524 * the SearchDialog will show them instead of the standard searches. 2525 * @param result String to show on the editable line of the SearchDialog. 2526 */ 2527 @Override 2528 public void showVoiceSearchResults(String result) { 2529 ContentProviderClient client = mActivity.getContentResolver() 2530 .acquireContentProviderClient(Browser.BOOKMARKS_URI); 2531 ContentProvider prov = client.getLocalContentProvider(); 2532 BrowserProvider bp = (BrowserProvider) prov; 2533 bp.setQueryResults(mTabControl.getCurrentTab().getVoiceSearchResults()); 2534 client.release(); 2535 2536 Bundle bundle = createGoogleSearchSourceBundle( 2537 GOOGLE_SEARCH_SOURCE_SEARCHKEY); 2538 bundle.putBoolean(SearchManager.CONTEXT_IS_VOICE, true); 2539 startSearch(result, false, bundle, false); 2540 } 2541 2542 private void startSearch(String initialQuery, boolean selectInitialQuery, 2543 Bundle appSearchData, boolean globalSearch) { 2544 if (appSearchData == null) { 2545 appSearchData = createGoogleSearchSourceBundle( 2546 GOOGLE_SEARCH_SOURCE_TYPE); 2547 } 2548 2549 SearchEngine searchEngine = mSettings.getSearchEngine(); 2550 if (searchEngine != null && !searchEngine.supportsVoiceSearch()) { 2551 appSearchData.putBoolean(SearchManager.DISABLE_VOICE_SEARCH, true); 2552 } 2553 mActivity.startSearch(initialQuery, selectInitialQuery, appSearchData, 2554 globalSearch); 2555 } 2556 2557 private Bundle createGoogleSearchSourceBundle(String source) { 2558 Bundle bundle = new Bundle(); 2559 bundle.putString(Search.SOURCE, source); 2560 return bundle; 2561 } 2562 2563 /** 2564 * helper method for key handler 2565 * returns the current tab if it can't advance 2566 */ 2567 private Tab getNextTab() { 2568 return mTabControl.getTab(Math.min(mTabControl.getTabCount() - 1, 2569 mTabControl.getCurrentPosition() + 1)); 2570 } 2571 2572 /** 2573 * helper method for key handler 2574 * returns the current tab if it can't advance 2575 */ 2576 private Tab getPrevTab() { 2577 return mTabControl.getTab(Math.max(0, 2578 mTabControl.getCurrentPosition() - 1)); 2579 } 2580 2581 /** 2582 * handle key events in browser 2583 * 2584 * @param keyCode 2585 * @param event 2586 * @return true if handled, false to pass to super 2587 */ 2588 boolean onKeyDown(int keyCode, KeyEvent event) { 2589 boolean noModifiers = event.hasNoModifiers(); 2590 // Even if MENU is already held down, we need to call to super to open 2591 // the IME on long press. 2592 if (!noModifiers 2593 && ((KeyEvent.KEYCODE_MENU == keyCode) 2594 || (KeyEvent.KEYCODE_CTRL_LEFT == keyCode) 2595 || (KeyEvent.KEYCODE_CTRL_RIGHT == keyCode))) { 2596 mMenuIsDown = true; 2597 return false; 2598 } 2599 2600 WebView webView = getCurrentTopWebView(); 2601 Tab tab = getCurrentTab(); 2602 if (webView == null || tab == null) return false; 2603 2604 boolean ctrl = event.hasModifiers(KeyEvent.META_CTRL_ON); 2605 boolean shift = event.hasModifiers(KeyEvent.META_SHIFT_ON); 2606 2607 switch(keyCode) { 2608 case KeyEvent.KEYCODE_TAB: 2609 if (event.isCtrlPressed()) { 2610 if (event.isShiftPressed()) { 2611 // prev tab 2612 switchToTab(getPrevTab()); 2613 } else { 2614 // next tab 2615 switchToTab(getNextTab()); 2616 } 2617 return true; 2618 } 2619 break; 2620 case KeyEvent.KEYCODE_SPACE: 2621 // WebView/WebTextView handle the keys in the KeyDown. As 2622 // the Activity's shortcut keys are only handled when WebView 2623 // doesn't, have to do it in onKeyDown instead of onKeyUp. 2624 if (shift) { 2625 pageUp(); 2626 } else if (noModifiers) { 2627 pageDown(); 2628 } 2629 return true; 2630 case KeyEvent.KEYCODE_BACK: 2631 if (!noModifiers) break; 2632 event.startTracking(); 2633 return true; 2634 case KeyEvent.KEYCODE_FORWARD: 2635 if (!noModifiers) break; 2636 tab.goForward(); 2637 return true; 2638 case KeyEvent.KEYCODE_DPAD_LEFT: 2639 if (ctrl) { 2640 tab.goBack(); 2641 return true; 2642 } 2643 break; 2644 case KeyEvent.KEYCODE_DPAD_RIGHT: 2645 if (ctrl) { 2646 tab.goForward(); 2647 return true; 2648 } 2649 break; 2650 case KeyEvent.KEYCODE_A: 2651 if (ctrl) { 2652 webView.selectAll(); 2653 return true; 2654 } 2655 break; 2656 // case KeyEvent.KEYCODE_B: // menu 2657 case KeyEvent.KEYCODE_C: 2658 if (ctrl) { 2659 webView.copySelection(); 2660 return true; 2661 } 2662 break; 2663 // case KeyEvent.KEYCODE_D: // menu 2664 // case KeyEvent.KEYCODE_E: // in Chrome: puts '?' in URL bar 2665 // case KeyEvent.KEYCODE_F: // menu 2666 // case KeyEvent.KEYCODE_G: // in Chrome: finds next match 2667 // case KeyEvent.KEYCODE_H: // menu 2668 // case KeyEvent.KEYCODE_I: // unused 2669 // case KeyEvent.KEYCODE_J: // menu 2670 // case KeyEvent.KEYCODE_K: // in Chrome: puts '?' in URL bar 2671 // case KeyEvent.KEYCODE_L: // menu 2672 // case KeyEvent.KEYCODE_M: // unused 2673 // case KeyEvent.KEYCODE_N: // in Chrome: new window 2674 // case KeyEvent.KEYCODE_O: // in Chrome: open file 2675 // case KeyEvent.KEYCODE_P: // in Chrome: print page 2676 // case KeyEvent.KEYCODE_Q: // unused 2677 // case KeyEvent.KEYCODE_R: 2678 // case KeyEvent.KEYCODE_S: // in Chrome: saves page 2679 case KeyEvent.KEYCODE_T: 2680 // we can't use the ctrl/shift flags, they check for 2681 // exclusive use of a modifier 2682 if (event.isCtrlPressed()) { 2683 if (event.isShiftPressed()) { 2684 openIncognitoTab(); 2685 } else { 2686 openTabToHomePage(); 2687 } 2688 return true; 2689 } 2690 break; 2691 // case KeyEvent.KEYCODE_U: // in Chrome: opens source of page 2692 // case KeyEvent.KEYCODE_V: // text view intercepts to paste 2693 // case KeyEvent.KEYCODE_W: // menu 2694 // case KeyEvent.KEYCODE_X: // text view intercepts to cut 2695 // case KeyEvent.KEYCODE_Y: // unused 2696 // case KeyEvent.KEYCODE_Z: // unused 2697 } 2698 // it is a regular key and webview is not null 2699 return mUi.dispatchKey(keyCode, event); 2700 } 2701 2702 boolean onKeyLongPress(int keyCode, KeyEvent event) { 2703 switch(keyCode) { 2704 case KeyEvent.KEYCODE_BACK: 2705 if (mUi.isWebShowing()) { 2706 bookmarksOrHistoryPicker(ComboViews.History); 2707 return true; 2708 } 2709 break; 2710 } 2711 return false; 2712 } 2713 2714 boolean onKeyUp(int keyCode, KeyEvent event) { 2715 if (KeyEvent.KEYCODE_MENU == keyCode) { 2716 mMenuIsDown = false; 2717 if (event.isTracking() && !event.isCanceled()) { 2718 return onMenuKey(); 2719 } 2720 } 2721 if (!event.hasNoModifiers()) return false; 2722 switch(keyCode) { 2723 case KeyEvent.KEYCODE_BACK: 2724 if (event.isTracking() && !event.isCanceled()) { 2725 onBackKey(); 2726 return true; 2727 } 2728 break; 2729 } 2730 return false; 2731 } 2732 2733 public boolean isMenuDown() { 2734 return mMenuIsDown; 2735 } 2736 2737 public void setupAutoFill(Message message) { 2738 // Open the settings activity at the AutoFill profile fragment so that 2739 // the user can create a new profile. When they return, we will dispatch 2740 // the message so that we can autofill the form using their new profile. 2741 Intent intent = new Intent(mActivity, BrowserPreferencesPage.class); 2742 intent.putExtra(PreferenceActivity.EXTRA_SHOW_FRAGMENT, 2743 AutoFillSettingsFragment.class.getName()); 2744 mAutoFillSetupMessage = message; 2745 mActivity.startActivityForResult(intent, AUTOFILL_SETUP); 2746 } 2747 2748 public boolean onSearchRequested() { 2749 mUi.editUrl(false); 2750 return true; 2751 } 2752 2753 @Override 2754 public boolean shouldCaptureThumbnails() { 2755 return mUi.shouldCaptureThumbnails(); 2756 } 2757 2758 @Override 2759 public void setBlockEvents(boolean block) { 2760 mBlockEvents = block; 2761 } 2762 2763 public boolean dispatchKeyEvent(KeyEvent event) { 2764 return mBlockEvents; 2765 } 2766 2767 public boolean dispatchKeyShortcutEvent(KeyEvent event) { 2768 return mBlockEvents; 2769 } 2770 2771 public boolean dispatchTouchEvent(MotionEvent ev) { 2772 return mBlockEvents; 2773 } 2774 2775 public boolean dispatchTrackballEvent(MotionEvent ev) { 2776 return mBlockEvents; 2777 } 2778 2779 public boolean dispatchGenericMotionEvent(MotionEvent ev) { 2780 return mBlockEvents; 2781 } 2782 2783 } 2784