import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteException;
import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.DrawFilter;
import android.graphics.Paint;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Browser;
-import android.provider.Contacts;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.Intents.Insert;
import android.provider.Downloads;
import android.provider.MediaStore;
-import android.provider.Contacts.Intents.Insert;
import android.text.IClipboard;
import android.text.TextUtils;
import android.text.format.DateFormat;
import android.text.util.Regex;
+import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.view.Gravity;
import java.util.zip.ZipFile;
public class BrowserActivity extends Activity
- implements KeyTracker.OnKeyTracker,
- View.OnCreateContextMenuListener,
+ implements View.OnCreateContextMenuListener,
DownloadListener {
/* Define some aliases to make these debugging flags easier to refer to.
mResolver = getContentResolver();
+ // If this was a web search request, pass it on to the default web
+ // search provider and finish this activity.
+ if (handleWebSearchIntent(getIntent())) {
+ finish();
+ return;
+ }
+
//
// start MASF proxy service
//
};
registerReceiver(mPackageInstallationReceiver, filter);
- // If this was a web search request, pass it on to the default web search provider.
- if (handleWebSearchIntent(getIntent())) {
- moveTaskToBack(true);
- return;
- }
-
if (!mTabControl.restoreState(icicle)) {
// clear up the thumbnail directory if we can't restore the state as
// none of the files in the directory are referenced any more.
|| Intent.ACTION_WEB_SEARCH.equals(action)) {
url = intent.getStringExtra(SearchManager.QUERY);
}
- return handleWebSearchRequest(url, intent.getBundleExtra(SearchManager.APP_DATA));
+ return handleWebSearchRequest(url, intent.getBundleExtra(SearchManager.APP_DATA),
+ intent.getStringExtra(SearchManager.EXTRA_DATA_KEY));
}
/**
* was identified as plain search terms and not URL/shortcut.
* @return true if the request was handled and web search activity was launched, false if not.
*/
- private boolean handleWebSearchRequest(String inUrl, Bundle appData) {
+ private boolean handleWebSearchRequest(String inUrl, Bundle appData, String extraData) {
if (inUrl == null) return false;
// In general, we shouldn't modify URL from Intent.
if (appData != null) {
intent.putExtra(SearchManager.APP_DATA, appData);
}
+ if (extraData != null) {
+ intent.putExtra(SearchManager.EXTRA_DATA_KEY, extraData);
+ }
intent.putExtra(Browser.EXTRA_APPLICATION_ID, getPackageName());
startActivity(intent);
return true;
}
+ /**
+ * Special class used exclusively for the shadow drawn underneath the fake
+ * title bar. The shadow does not need to be drawn if the WebView
+ * underneath is scrolled to the top, because it will draw directly on top
+ * of the embedded shadow.
+ */
+ private static class Shadow extends View {
+ private WebView mWebView;
+
+ public Shadow(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void setWebView(WebView view) {
+ mWebView = view;
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ // In general onDraw is the method to override, but we care about
+ // whether or not the background gets drawn, which happens in draw()
+ if (mWebView == null || mWebView.getScrollY() > getHeight()) {
+ super.draw(canvas);
+ }
+ // Need to invalidate so that if the scroll position changes, we
+ // still draw as appropriate.
+ invalidate();
+ }
+ }
+
private void showFakeTitleBar() {
+ final View decor = getWindow().peekDecorView();
if (mFakeTitleBar == null && mActiveTabsPage == null
- && !mActivityInPause) {
+ && !mActivityInPause && decor != null
+ && decor.getWindowToken() != null) {
+ Rect visRect = new Rect();
+ if (!mBrowserFrameLayout.getGlobalVisibleRect(visRect)) {
+ if (LOGD_ENABLED) {
+ Log.d(LOGTAG, "showFakeTitleBar visRect failed");
+ }
+ return;
+ }
final WebView webView = getTopWindow();
mFakeTitleBar = new TitleBar(this);
mFakeTitleBar.setTitleAndUrl(null, webView.getUrl());
ViewGroup.LayoutParams.WRAP_CONTENT,
WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
- PixelFormat.OPAQUE);
+ PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;
WebView mainView = mTabControl.getCurrentWebView();
- params.windowAnimations = mainView == null
- || mainView.getScrollY() != 0
- ? com.android.internal.R.style.Animation_DropDownDown : 0;
+ boolean atTop = mainView != null && mainView.getScrollY() == 0;
+ params.windowAnimations = atTop ? 0
+ : com.android.internal.R.style.Animation_DropDownDown;
// XXX : Without providing an offset, the fake title bar will be
// placed underneath the status bar. Use the global visible rect
// of mBrowserFrameLayout to determine the bottom of the status bar
- Rect rectangle = new Rect();
- mBrowserFrameLayout.getGlobalVisibleRect(rectangle);
- params.y = rectangle.top;
- // Add a holder for the title bar. It is a FrameLayout, which
- // allows it to have an overlay shadow. It also has a white
- // background, which is the same as the background when it is
- // placed in a WebView.
+ params.y = visRect.top;
+ // Add a holder for the title bar. It also holds a shadow to show
+ // below the title bar.
if (mFakeTitleBarHolder == null) {
mFakeTitleBarHolder = (ViewGroup) LayoutInflater.from(this)
.inflate(R.layout.title_bar_bg, null);
}
- mFakeTitleBarHolder.addView(mFakeTitleBar, mFakeTitleBarParams);
+ Shadow shadow = (Shadow) mFakeTitleBarHolder.findViewById(
+ R.id.shadow);
+ shadow.setWebView(mainView);
+ mFakeTitleBarHolder.addView(mFakeTitleBar, 0, mFakeTitleBarParams);
manager.addView(mFakeTitleBarHolder, params);
}
}
}
private void hideFakeTitleBar() {
if (mFakeTitleBar == null) return;
+ WindowManager.LayoutParams params = (WindowManager.LayoutParams)
+ mFakeTitleBarHolder.getLayoutParams();
+ WebView mainView = mTabControl.getCurrentWebView();
+ // Although we decided whether or not to animate based on the current
+ // scroll position, the scroll position may have changed since the
+ // fake title bar was displayed. Make sure it has the appropriate
+ // animation/lack thereof before removing.
+ params.windowAnimations = mainView != null && mainView.getScrollY() == 0
+ ? 0 : com.android.internal.R.style.Animation_DropDownDown;
WindowManager manager
= (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+ manager.updateViewLayout(mFakeTitleBarHolder, params);
mFakeTitleBarHolder.removeView(mFakeTitleBar);
manager.removeView(mFakeTitleBarHolder);
mFakeTitleBar = null;
}
/**
+ * Special method for the fake title bar to call when displaying its context
+ * menu, since it is in its own Window, and its parent does not show a
+ * context menu.
+ */
+ /* package */ void showTitleBarContextMenu() {
+ if (null == mTitleBar.getParent()) {
+ return;
+ }
+ openContextMenu(mTitleBar);
+ }
+
+ /**
* onSaveInstanceState(Bundle map)
* onSaveInstanceState is called right before onStop(). The map contains
* the saved state.
Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this);
}
super.onDestroy();
+
+ if (mTabControl == null) return;
+
// Remove the current tab and sub window
TabControl.Tab t = mTabControl.getCurrentTab();
if (t != null) {
// options selector, so set mCanChord to true so we can access them.
mCanChord = true;
int id = item.getItemId();
- final WebView webView = getTopWindow();
- if (null == webView) {
- return false;
- }
- final HashMap hrefMap = new HashMap();
- hrefMap.put("webview", webView);
- final Message msg = mHandler.obtainMessage(
- FOCUS_NODE_HREF, id, 0, hrefMap);
switch (id) {
+ // For the context menu from the title bar
+ case R.id.title_bar_share_page_url:
+ case R.id.title_bar_copy_page_url:
+ WebView mainView = mTabControl.getCurrentWebView();
+ if (null == mainView) {
+ return false;
+ }
+ if (id == R.id.title_bar_share_page_url) {
+ Browser.sendString(this, mainView.getUrl());
+ } else {
+ copy(mainView.getUrl());
+ }
+ break;
// -- Browser context menu
case R.id.open_context_menu_id:
case R.id.open_newtab_context_menu_id:
case R.id.save_link_context_menu_id:
case R.id.share_link_context_menu_id:
case R.id.copy_link_context_menu_id:
+ final WebView webView = getTopWindow();
+ if (null == webView) {
+ return false;
+ }
+ final HashMap hrefMap = new HashMap();
+ hrefMap.put("webview", webView);
+ final Message msg = mHandler.obtainMessage(
+ FOCUS_NODE_HREF, id, 0, hrefMap);
webView.requestFocusNodeHref(msg);
break;
break;
case R.id.goto_menu_id:
+ onSearchRequested();
+ break;
+
+ case R.id.bookmarks_menu_id:
bookmarksOrHistoryPicker(false);
break;
i.putExtra("url", w.getUrl());
i.putExtra("title", w.getTitle());
i.putExtra("touch_icon_url", w.getTouchIconUrl());
+ i.putExtra("thumbnail", createScreenshot(w));
startActivity(i);
break;
break;
case R.id.share_page_menu_id:
- Browser.sendString(this, getTopWindow().getUrl());
+ Browser.sendString(this, getTopWindow().getUrl(),
+ getText(R.string.choosertitle_sharevia).toString());
break;
case R.id.dump_nav_menu_id:
.parse(WebView.SCHEME_TEL + extra)));
Intent addIntent = new Intent(Intent.ACTION_INSERT_OR_EDIT);
addIntent.putExtra(Insert.PHONE, Uri.decode(extra));
- addIntent.setType(Contacts.People.CONTENT_ITEM_TYPE);
+ addIntent.setType(ContactsContract.Contacts.CONTENT_ITEM_TYPE);
menu.findItem(R.id.add_contact_context_menu_id).setIntent(
addIntent);
menu.findItem(R.id.copy_phone_context_menu_id).setOnMenuItemClickListener(
}
// Attach the given tab to the content view.
+ // this should only be called for the current tab.
private void attachTabToContentView(TabControl.Tab t) {
// Attach the container that contains the main WebView and any other UI
// associated with the tab.
ViewGroup.LayoutParams.WRAP_CONTENT));
}
- if (t == mTabControl.getCurrentTab()) {
- setLockIconType(t.getLockIconType());
- setPrevLockType(t.getPrevLockIconType());
+ setLockIconType(t.getLockIconType());
+ setPrevLockType(t.getPrevLockIconType());
+
+ // this is to match the code in removeTabFromContentView()
+ if (!mPageStarted && t.getTopWindow().getProgress() < 100) {
+ mPageStarted = true;
}
WebView view = t.getWebView();
view.setEmbeddedTitleBar(null);
}
+ // unlike attachTabToContentView(), removeTabFromContentView() can be
+ // called for the non-current tab. Need to add the check.
if (t == mTabControl.getCurrentTab()) {
t.setLockIconType(getLockIconType());
t.setPrevLockIconType(getPrevLockType());
+
+ // this is not a perfect solution. But currently there is one
+ // WebViewClient for all the WebView. if user switches from an
+ // in-load window to an already loaded window, mPageStarted will not
+ // be set to false. If user leaves the Browser, pauseWebViewTimers()
+ // won't do anything and leaves the timer running even Browser is in
+ // the background.
+ if (mPageStarted) {
+ mPageStarted = false;
+ }
}
}
currentIndex--;
}
mTabControl.setCurrentTab(mTabControl.getTab(currentIndex));
+ resetTitleIconAndProgress();
}
private void goBackOnePageOrQuit() {
* moveTaskToBack().
*/
moveTaskToBack(true);
+ return;
}
WebView w = current.getWebView();
if (w.canGoBack()) {
}
}
- public KeyTracker.State onKeyTracker(int keyCode,
- KeyEvent event,
- KeyTracker.Stage stage,
- int duration) {
- // if onKeyTracker() is called after activity onStop()
- // because of accumulated key events,
- // we should ignore it as browser is not active any more.
- WebView topWindow = getTopWindow();
- if (topWindow == null && mCustomView == null)
- return KeyTracker.State.NOT_TRACKING;
-
- if (keyCode == KeyEvent.KEYCODE_BACK) {
- // Check if a custom view is currently showing and, if it is, hide it.
- if (mCustomView != null) {
- mWebChromeClient.onHideCustomView();
- return KeyTracker.State.DONE_TRACKING;
- }
- if (stage == KeyTracker.Stage.LONG_REPEAT) {
- bookmarksOrHistoryPicker(true);
- return KeyTracker.State.DONE_TRACKING;
- } else if (stage == KeyTracker.Stage.UP) {
- // FIXME: Currently, we do not have a notion of the
- // history picker for the subwindow, but maybe we
- // should?
- WebView subwindow = mTabControl.getCurrentSubWindow();
- if (subwindow != null) {
- if (subwindow.canGoBack()) {
- subwindow.goBack();
- } else {
- dismissSubWindow(mTabControl.getCurrentTab());
- }
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // The default key mode is DEFAULT_KEYS_SEARCH_LOCAL. As the MENU is
+ // still down, we don't want to trigger the search. Pretend to consume
+ // the key and do nothing.
+ if (mMenuIsDown) return true;
+
+ switch(keyCode) {
+ case KeyEvent.KEYCODE_MENU:
+ mMenuIsDown = true;
+ break;
+ case KeyEvent.KEYCODE_SPACE:
+ // WebView/WebTextView handle the keys in the KeyDown. As
+ // the Activity's shortcut keys are only handled when WebView
+ // doesn't, have to do it in onKeyDown instead of onKeyUp.
+ if (event.isShiftPressed()) {
+ getTopWindow().pageUp(false);
} else {
- goBackOnePageOrQuit();
+ getTopWindow().pageDown(false);
}
- return KeyTracker.State.DONE_TRACKING;
- }
- return KeyTracker.State.KEEP_TRACKING;
+ return true;
+ case KeyEvent.KEYCODE_BACK:
+ if (event.getRepeatCount() == 0) {
+ event.startTracking();
+ return true;
+ } else if (mCustomView == null && mActiveTabsPage == null
+ && event.isLongPress()) {
+ bookmarksOrHistoryPicker(true);
+ return true;
+ }
+ break;
}
- return KeyTracker.State.NOT_TRACKING;
+ return super.onKeyDown(keyCode, event);
}
- @Override public boolean onKeyDown(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_MENU) {
- mMenuIsDown = true;
- } else if (mMenuIsDown) {
- // The default key mode is DEFAULT_KEYS_SEARCH_LOCAL. As the MENU is
- // still down, we don't want to trigger the search. Pretend to
- // consume the key and do nothing.
- return true;
- }
- boolean handled = mKeyTracker.doKeyDown(keyCode, event);
- if (!handled) {
- switch (keyCode) {
- case KeyEvent.KEYCODE_SPACE:
- if (event.isShiftPressed()) {
- getTopWindow().pageUp(false);
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ switch(keyCode) {
+ case KeyEvent.KEYCODE_MENU:
+ mMenuIsDown = false;
+ break;
+ case KeyEvent.KEYCODE_BACK:
+ if (event.isTracking() && !event.isCanceled()) {
+ if (mCustomView != null) {
+ // if a custom view is showing, hide it
+ mWebChromeClient.onHideCustomView();
+ } else if (mActiveTabsPage != null) {
+ // if tab page is showing, hide it
+ removeActiveTabPage(true);
} else {
- getTopWindow().pageDown(false);
+ WebView subwindow = mTabControl.getCurrentSubWindow();
+ if (subwindow != null) {
+ if (subwindow.canGoBack()) {
+ subwindow.goBack();
+ } else {
+ dismissSubWindow(mTabControl.getCurrentTab());
+ }
+ } else {
+ goBackOnePageOrQuit();
+ }
}
- handled = true;
- break;
-
- default:
- break;
- }
- }
- return handled || super.onKeyDown(keyCode, event);
- }
-
- @Override public boolean onKeyUp(int keyCode, KeyEvent event) {
- if (keyCode == KeyEvent.KEYCODE_MENU) {
- mMenuIsDown = false;
+ return true;
+ }
+ break;
}
- return mKeyTracker.doKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
+ return super.onKeyUp(keyCode, event);
}
/* package */ void stopLoading() {
+ mDidStopLoad = true;
resetTitleAndRevertLockIcon();
WebView w = getTopWindow();
w.stopLoading();
private static final int CANCEL_CREDS_REQUEST = 103;
private static final int RELEASE_WAKELOCK = 107;
+ private static final int UPDATE_BOOKMARK_THUMBNAIL = 108;
+
// Private handler for handling javascript and saving passwords
private Handler mHandler = new Handler() {
public void handleMessage(Message msg) {
switch (msg.what) {
case FOCUS_NODE_HREF:
+ {
String url = (String) msg.getData().get("url");
if (url == null || url.length() == 0) {
break;
startActivity(intent);
break;
case R.id.share_link_context_menu_id:
- Browser.sendString(BrowserActivity.this, url);
+ Browser.sendString(BrowserActivity.this, url,
+ getText(R.string.choosertitle_sharevia).toString());
break;
case R.id.copy_link_context_menu_id:
copy(url);
break;
}
break;
+ }
case LOAD_URL:
loadURL(getTopWindow(), (String) msg.obj);
mWakeLock.release();
}
break;
+
+ case UPDATE_BOOKMARK_THUMBNAIL:
+ WebView view = (WebView) msg.obj;
+ if (view != null) {
+ updateScreenshot(view);
+ }
+ break;
}
}
};
// FIXME: Would like to make sure there is actually something to
// draw, but the API for that (WebViewCore.pictureReady()) is not
// currently accessible here.
+
ContentResolver cr = getContentResolver();
final Cursor c = BrowserBookmarksAdapter.queryBookmarksForUrl(
- cr, view.getOriginalUrl(), view.getUrl(), false);
+ cr, view.getOriginalUrl(), view.getUrl(), true);
if (c != null) {
boolean succeed = c.moveToFirst();
ContentValues values = null;
if (values == null) {
final ByteArrayOutputStream os
= new ByteArrayOutputStream();
- Picture thumbnail = view.capturePicture();
- // Keep width and height in sync with BrowserBookmarksPage
- // and bookmark_thumb
- Bitmap bm = Bitmap.createBitmap(100, 80,
- Bitmap.Config.ARGB_4444);
- Canvas canvas = new Canvas(bm);
- // May need to tweak these values to determine what is the
- // best scale factor
- canvas.scale(.5f, .5f);
- thumbnail.draw(canvas);
+ Bitmap bm = createScreenshot(view);
bm.compress(Bitmap.CompressFormat.PNG, 100, os);
values = new ContentValues();
values.put(Browser.BookmarkColumns.THUMBNAIL,
}
}
+ /**
+ * Values for the size of the thumbnail created when taking a screenshot.
+ * Lazily initialized. Instead of using these directly, use
+ * getDesiredThumbnailWidth() or getDesiredThumbnailHeight().
+ */
+ private static int THUMBNAIL_WIDTH = 0;
+ private static int THUMBNAIL_HEIGHT = 0;
+
+ /**
+ * Return the desired width for thumbnail screenshots, which are stored in
+ * the database, and used on the bookmarks screen.
+ * @param context Context for finding out the density of the screen.
+ * @return int desired width for thumbnail screenshot.
+ */
+ /* package */ static int getDesiredThumbnailWidth(Context context) {
+ if (THUMBNAIL_WIDTH == 0) {
+ float density = context.getResources().getDisplayMetrics().density;
+ THUMBNAIL_WIDTH = (int) (90 * density);
+ THUMBNAIL_HEIGHT = (int) (80 * density);
+ }
+ return THUMBNAIL_WIDTH;
+ }
+
+ /**
+ * Return the desired height for thumbnail screenshots, which are stored in
+ * the database, and used on the bookmarks screen.
+ * @param context Context for finding out the density of the screen.
+ * @return int desired height for thumbnail screenshot.
+ */
+ /* package */ static int getDesiredThumbnailHeight(Context context) {
+ // To ensure that they are both initialized.
+ getDesiredThumbnailWidth(context);
+ return THUMBNAIL_HEIGHT;
+ }
+
+ private Bitmap createScreenshot(WebView view) {
+ Picture thumbnail = view.capturePicture();
+ Bitmap bm = Bitmap.createBitmap(getDesiredThumbnailWidth(this),
+ getDesiredThumbnailHeight(this), Bitmap.Config.ARGB_4444);
+ Canvas canvas = new Canvas(bm);
+ // May need to tweak these values to determine what is the
+ // best scale factor
+ int thumbnailWidth = thumbnail.getWidth();
+ if (thumbnailWidth > 0) {
+ float scaleFactor = (float) getDesiredThumbnailWidth(this) /
+ (float)thumbnailWidth;
+ canvas.scale(scaleFactor, scaleFactor);
+ }
+ thumbnail.draw(canvas);
+ return bm;
+ }
+
// -------------------------------------------------------------------------
// WebViewClient implementation.
//-------------------------------------------------------------------------
private void updateIcon(WebView view, Bitmap icon) {
if (icon != null) {
BrowserBookmarksAdapter.updateBookmarkFavicon(mResolver,
- view, icon);
+ view.getOriginalUrl(), view.getUrl(), icon);
+ }
+ setFavicon(icon);
+ }
+
+ private void updateIcon(String url, Bitmap icon) {
+ if (icon != null) {
+ BrowserBookmarksAdapter.updateBookmarkFavicon(mResolver,
+ null, url, icon);
}
setFavicon(icon);
}
resetLockIcon(url);
setUrlTitle(url, null);
+ // We've started to load a new page. If there was a pending message
+ // to save a screenshot then we will now take the new page and
+ // save an incorrect screenshot. Therefore, remove any pending
+ // thumbnail messages from the queue.
+ mHandler.removeMessages(UPDATE_BOOKMARK_THUMBNAIL);
+
+ // If we start a touch icon load and then load a new page, we don't
+ // want to cancel the current touch icon loader. But, we do want to
+ // create a new one when the touch icon url is known.
+ if (mTouchIconLoader != null) {
+ mTouchIconLoader.mActivity = null;
+ mTouchIconLoader = null;
+ }
+
ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(false);
if (errorConsole != null) {
errorConsole.clearErrorMessages();
// Call updateIcon instead of setFavicon so the bookmark
// database can be updated.
- updateIcon(view, favicon);
+ updateIcon(url, favicon);
if (mSettings.isTracing()) {
String host;
CookieSyncManager.getInstance().resetSync();
mInLoad = true;
- WebView currentWebView = mTabControl.getCurrentWebView();
- if (currentWebView == null || currentWebView.getScrollY() != 0) {
- // This page has begun to load, so show the title bar
- showFakeTitleBar();
- }
+ mDidStopLoad = false;
+ showFakeTitleBar();
updateInLoadMenuItems();
if (!mIsNetworkUp) {
createAndShowNetworkDialog();
// load.
resetTitleAndIcon(view);
+ if (!mDidStopLoad) {
+ // Only update the bookmark screenshot if the user did not
+ // cancel the load early.
+ Message updateScreenshot = Message.obtain(mHandler, UPDATE_BOOKMARK_THUMBNAIL, view);
+ mHandler.sendMessageDelayed(updateScreenshot, 500);
+ }
+
// Update the lock icon image only once we are done loading
updateLockIconToLatest();
- updateScreenshot(view);
// Performance probe
if (false) {
}
if (newProgress == 100) {
- // onProgressChanged() is called for sub-frame too while
- // onPageFinished() is only called for the main frame. sync
- // cookie and cache promptly here.
+ // onProgressChanged() may continue to be called after the main
+ // frame has finished loading, as any remaining sub frames
+ // continue to load. We'll only get called once though with
+ // newProgress as 100 when everything is loaded.
+ // (onPageFinished is called once when the main frame completes
+ // loading regardless of the state of any sub frames so calls
+ // to onProgressChanges may continue after onPageFinished has
+ // executed)
+
+ // sync cookies and cache promptly here.
CookieSyncManager.getInstance().sync();
if (mInLoad) {
mInLoad = false;
// and update the menu items.
mInLoad = true;
updateInLoadMenuItems();
- WebView currentWebView = mTabControl.getCurrentWebView();
- if ((currentWebView == null || currentWebView.getScrollY() != 0)
- && (!mOptionsMenuOpen || mIconView)) {
+ if (!mOptionsMenuOpen || mIconView) {
// This page has begun to load, so show the title bar
showFakeTitleBar();
}
}
@Override
- public void onReceivedTouchIconUrl(WebView view, String url) {
+ public void onReceivedTouchIconUrl(WebView view, String url,
+ boolean precomposed) {
final ContentResolver cr = getContentResolver();
final Cursor c =
BrowserBookmarksAdapter.queryBookmarksForUrl(cr,
view.getOriginalUrl(), view.getUrl(), true);
if (c != null) {
if (c.getCount() > 0) {
- new DownloadTouchIcon(cr, c, view).execute(url);
+ // Let precomposed icons take precedence over non-composed
+ // icons.
+ if (precomposed && mTouchIconLoader != null) {
+ mTouchIconLoader.cancel(false);
+ mTouchIconLoader = null;
+ }
+ // Have only one async task at a time.
+ if (mTouchIconLoader == null) {
+ mTouchIconLoader = new DownloadTouchIcon(
+ BrowserActivity.this, cr, c, view);
+ mTouchIconLoader.execute(url);
+ }
} else {
c.close();
}
}
Log.w(LOGTAG, "Console: " + message + " " + sourceID + ":" + lineNumber);
}
+
+ /**
+ * Ask the browser for an icon to represent a <video> element.
+ * This icon will be used if the Web page did not specify a poster attribute.
+ *
+ * @return Bitmap The icon or null if no such icon is available.
+ * @hide pending API Council approval
+ */
+ @Override
+ public Bitmap getDefaultVideoPoster() {
+ if (mDefaultVideoPoster == null) {
+ mDefaultVideoPoster = BitmapFactory.decodeResource(
+ getResources(), R.drawable.default_video_poster);
+ }
+ return mDefaultVideoPoster;
+ }
+
+ /**
+ * Ask the host application for a custom progress view to show while
+ * a <video> is loading.
+ *
+ * @return View The progress view.
+ * @hide pending API Council approval
+ */
+ @Override
+ public View getVideoLoadingProgressView() {
+ if (mVideoProgressView == null) {
+ LayoutInflater inflater = LayoutInflater.from(BrowserActivity.this);
+ mVideoProgressView = inflater.inflate(R.layout.video_loading_progress, null);
+ }
+ return mVideoProgressView;
+ }
};
/**
CombinedBookmarkHistoryActivity.class);
String title = current.getTitle();
String url = current.getUrl();
+ Bitmap thumbnail = createScreenshot(current);
+
// Just in case the user opens bookmarks before a page finishes loading
// so the current history item, and therefore the page, is null.
if (null == url) {
}
intent.putExtra("title", title);
intent.putExtra("url", url);
+ intent.putExtra("thumbnail", thumbnail);
// Disable opening in a new window if we have maxed out the windows
intent.putExtra("disable_new_window", mTabControl.getTabCount()
>= TabControl.MAX_TABS);
private boolean mInLoad;
private boolean mIsNetworkUp;
+ private boolean mDidStopLoad;
private boolean mPageStarted;
private boolean mActivityInPause = true;
private boolean mMenuIsDown;
- private final KeyTracker mKeyTracker = new KeyTracker(this);
-
- // As trackball doesn't send repeat down, we have to track it ourselves
- private boolean mTrackTrackball;
-
private static boolean mInTrace;
// Performance probe
private BroadcastReceiver mPackageInstallationReceiver;
+ // AsyncTask for downloading touch icons
+ /* package */ DownloadTouchIcon mTouchIconLoader;
+
// activity requestCode
final static int COMBO_PAGE = 1;
final static int DOWNLOAD_PAGE = 2;
final static int PREFERENCES_PAGE = 3;
+ // the default <video> poster
+ private Bitmap mDefaultVideoPoster;
+ // the video progress view
+ private View mVideoProgressView;
+
/**
* A UrlData class to abstract how the content will be set to WebView.
* This base class uses loadUrl to show the content.