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