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