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.Color;
import android.graphics.DrawFilter;
import android.graphics.Paint;
import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Picture;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-import android.graphics.drawable.LayerDrawable;
-import android.graphics.drawable.PaintDrawable;
import android.hardware.SensorListener;
import android.hardware.SensorManager;
import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.net.Uri;
import android.net.WebAddress;
import android.net.http.EventHandler;
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.
}
}
- // Flag to enable the touchable browser bar with buttons
- private final boolean CUSTOM_BROWSER_BAR = true;
+ /**
+ * This layout holds everything you see below the status bar, including the
+ * error console, the custom view container, and the webviews.
+ */
+ private FrameLayout mBrowserFrameLayout;
@Override public void onCreate(Bundle icicle) {
if (LOGV_ENABLED) {
Log.v(LOGTAG, this + " onStart");
}
super.onCreate(icicle);
- if (CUSTOM_BROWSER_BAR) {
- this.requestWindowFeature(Window.FEATURE_NO_TITLE);
- } else {
- this.requestWindowFeature(Window.FEATURE_LEFT_ICON);
- this.requestWindowFeature(Window.FEATURE_RIGHT_ICON);
- this.requestWindowFeature(Window.FEATURE_PROGRESS);
- this.requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS);
- }
// test the browser in OpenGL
// requestWindowFeature(Window.FEATURE_OPENGL);
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
//
android.R.drawable.ic_secure);
mMixLockIcon = Resources.getSystem().getDrawable(
android.R.drawable.ic_partial_secure);
- mGenericFavicon = getResources().getDrawable(
- R.drawable.app_web_browser_sm);
FrameLayout frameLayout = (FrameLayout) getWindow().getDecorView()
.findViewById(com.android.internal.R.id.content);
- if (CUSTOM_BROWSER_BAR) {
- // This FrameLayout will hold the custom FrameLayout and a LinearLayout
- // that contains the title bar and a FrameLayout, which
- // holds everything else.
- FrameLayout browserFrameLayout = (FrameLayout) LayoutInflater.from(this)
- .inflate(R.layout.custom_screen, null);
- mTitleBar = (TitleBarSet) browserFrameLayout.findViewById(R.id.title_bar);
- mContentView = (FrameLayout) browserFrameLayout.findViewById(
- R.id.main_content);
- mErrorConsoleContainer = (LinearLayout) browserFrameLayout.findViewById(
- R.id.error_console);
- mCustomViewContainer = (FrameLayout) browserFrameLayout
- .findViewById(R.id.fullscreen_custom_content);
- frameLayout.addView(browserFrameLayout, COVER_SCREEN_PARAMS);
- } else {
- mCustomViewContainer = new FrameLayout(this);
- mCustomViewContainer.setBackgroundColor(Color.BLACK);
- mContentView = new FrameLayout(this);
-
- LinearLayout linearLayout = new LinearLayout(this);
- linearLayout.setOrientation(LinearLayout.VERTICAL);
- mErrorConsoleContainer = new LinearLayout(this);
- linearLayout.addView(mErrorConsoleContainer, new LinearLayout.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
- linearLayout.addView(mContentView, COVER_SCREEN_PARAMS);
- frameLayout.addView(mCustomViewContainer, COVER_SCREEN_PARAMS);
- frameLayout.addView(linearLayout, COVER_SCREEN_PARAMS);
- }
+ mBrowserFrameLayout = (FrameLayout) LayoutInflater.from(this)
+ .inflate(R.layout.custom_screen, null);
+ mContentView = (FrameLayout) mBrowserFrameLayout.findViewById(
+ R.id.main_content);
+ mErrorConsoleContainer = (LinearLayout) mBrowserFrameLayout
+ .findViewById(R.id.error_console);
+ mCustomViewContainer = (FrameLayout) mBrowserFrameLayout
+ .findViewById(R.id.fullscreen_custom_content);
+ frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);
+ mTitleBar = new TitleBar(this);
// Create the tab control and our initial tab
mTabControl = new TabControl(this);
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
ConnectivityManager.CONNECTIVITY_ACTION)) {
- boolean down = intent.getBooleanExtra(
- ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- onNetworkToggle(!down);
+ NetworkInfo info =
+ (NetworkInfo) intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ onNetworkToggle(
+ (info != null) ? info.isConnected() : false);
}
}
};
};
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.
}
if (urlData.isEmpty()) {
- bookmarksOrHistoryPicker(false, true);
+ if (mSettings.isLoginInitialized()) {
+ webView.loadUrl(mSettings.getHomePage());
+ } else {
+ waitForCredentials();
+ }
} else {
if (extra != null) {
urlData.setPostData(extra
attachTabToContentView(mTabControl.getCurrentTab());
}
- if (CUSTOM_BROWSER_BAR) {
- // Create title bars for all of the tabs that have been created
- for (int i = 0; i < mTabControl.getTabCount(); i ++) {
- WebView view = mTabControl.getTab(i).getWebView();
- mTitleBar.addTab(view, false);
- }
-
- mTitleBar.setBrowserActivity(this);
- mTitleBar.setCurrentTab(mTabControl.getCurrentIndex());
- }
-
// Read JavaScript flags if it exists.
String jsFlags = mSettings.getJsFlags();
if (jsFlags.trim().length() != 0) {
return;
}
mTabControl.setCurrentTab(current);
- if (CUSTOM_BROWSER_BAR) {
- mTitleBar.setCurrentTab(mTabControl.getTabIndex(current));
- }
attachTabToContentView(current);
resetTitleAndIcon(current.getWebView());
}
// No matching application tab, try to find a regular tab
// with a matching url.
appTab = mTabControl.findUnusedTabWithUrl(urlData.mUrl);
- if (appTab != null && current != appTab) {
- switchToTab(mTabControl.getTabIndex(appTab));
+ if (appTab != null) {
+ if (current != appTab) {
+ switchToTab(mTabControl.getTabIndex(appTab));
+ }
+ // Otherwise, we are already viewing the correct tab.
} else {
// if FLAG_ACTIVITY_BROUGHT_TO_FRONT flag is on, the url
// will be opened in a new tab unless we have reached
|| 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);
}
/* package */ static String fixUrl(String inUrl) {
+ // FIXME: Converting the url to lower case
+ // duplicates functionality in smartUrlFilter().
+ // However, changing all current callers of fixUrl to
+ // call smartUrlFilter in addition may have unwanted
+ // consequences, and is deferred for now.
+ int colon = inUrl.indexOf(':');
+ boolean allLower = true;
+ for (int index = 0; index < colon; index++) {
+ char ch = inUrl.charAt(index);
+ if (!Character.isLetter(ch)) {
+ break;
+ }
+ allLower &= Character.isLowerCase(ch);
+ if (index == colon - 1 && !allLower) {
+ inUrl = inUrl.substring(0, colon).toLowerCase()
+ + inUrl.substring(colon);
+ }
+ }
if (inUrl.startsWith("http://") || inUrl.startsWith("https://"))
return inUrl;
if (inUrl.startsWith("http:") ||
}
/**
+ * Since the actual title bar is embedded in the WebView, and removing it
+ * would change its appearance, create a temporary title bar to go at
+ * the top of the screen while the menu is open.
+ */
+ private TitleBar mFakeTitleBar;
+
+ /**
+ * Holder for the fake title bar. It will have a foreground shadow, as well
+ * as a white background, so the fake title bar looks like the real one.
+ */
+ private ViewGroup mFakeTitleBarHolder;
+
+ /**
+ * Layout parameters for the fake title bar within mFakeTitleBarHolder
+ */
+ private FrameLayout.LayoutParams mFakeTitleBarParams
+ = new FrameLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ /**
+ * Keeps track of whether the options menu is open. This is important in
+ * determining whether to show or hide the title bar overlay.
+ */
+ private boolean mOptionsMenuOpen;
+
+ /**
+ * Only meaningful when mOptionsMenuOpen is true. This variable keeps track
+ * of whether the configuration has changed. The first onMenuOpened call
+ * after a configuration change is simply a reopening of the same menu
+ * (i.e. mIconView did not change).
+ */
+ private boolean mConfigChanged;
+
+ /**
+ * Whether or not the options menu is in its smaller, icon menu form. When
+ * true, we want the title bar overlay to be up. When false, we do not.
+ * Only meaningful if mOptionsMenuOpen is true.
+ */
+ private boolean mIconView;
+
+ @Override
+ public boolean onMenuOpened(int featureId, Menu menu) {
+ if (Window.FEATURE_OPTIONS_PANEL == featureId) {
+ if (mOptionsMenuOpen) {
+ if (mConfigChanged) {
+ // We do not need to make any changes to the state of the
+ // title bar, since the only thing that happened was a
+ // change in orientation
+ mConfigChanged = false;
+ } else {
+ if (mIconView) {
+ // Switching the menu to expanded view, so hide the
+ // title bar.
+ hideFakeTitleBar();
+ mIconView = false;
+ } else {
+ // Switching the menu back to icon view, so show the
+ // title bar once again.
+ showFakeTitleBar();
+ mIconView = true;
+ }
+ }
+ } else {
+ // The options menu is closed, so open it, and show the title
+ showFakeTitleBar();
+ mOptionsMenuOpen = true;
+ mConfigChanged = false;
+ mIconView = true;
+ }
+ }
+ 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 && 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());
+ mFakeTitleBar.setProgress(webView.getProgress());
+ mFakeTitleBar.setFavicon(webView.getFavicon());
+ updateLockIconToLatest();
+
+ WindowManager manager
+ = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
+
+ // Add the title bar to the window manager so it can receive touches
+ // while the menu is up
+ WindowManager.LayoutParams params
+ = new WindowManager.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
+ WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
+ PixelFormat.TRANSLUCENT);
+ params.gravity = Gravity.TOP;
+ WebView mainView = mTabControl.getCurrentWebView();
+ 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
+ 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);
+ }
+ Shadow shadow = (Shadow) mFakeTitleBarHolder.findViewById(
+ R.id.shadow);
+ shadow.setWebView(mainView);
+ mFakeTitleBarHolder.addView(mFakeTitleBar, 0, mFakeTitleBarParams);
+ manager.addView(mFakeTitleBarHolder, params);
+ }
+ }
+
+ @Override
+ public void onOptionsMenuClosed(Menu menu) {
+ mOptionsMenuOpen = false;
+ if (!mInLoad) {
+ hideFakeTitleBar();
+ } else if (!mIconView) {
+ // The page is currently loading, and we are in expanded mode, so
+ // we were not showing the menu. Show it once again. It will be
+ // removed when the page finishes.
+ showFakeTitleBar();
+ }
+ }
+ 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.
}
mCredsDlg = null;
+ // FIXME: This removes the active tabs page and resets the menu to
+ // MAIN_MENU. A better solution might be to do this work in onNewIntent
+ // but then we would need to save it in onSaveInstanceState and restore
+ // it in onCreate/onRestoreInstanceState
+ if (mActiveTabsPage != null) {
+ removeActiveTabPage(true);
+ }
+
cancelStopToast();
// unregister network state listener
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) {
@Override
public void onConfigurationChanged(Configuration newConfig) {
+ mConfigChanged = true;
super.onConfigurationChanged(newConfig);
if (mPageInfoDialog != 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;
*/
@Override
public boolean onSearchRequested() {
+ if (mOptionsMenuOpen) closeOptionsMenu();
String url = (getTopWindow() == null) ? null : getTopWindow().getUrl();
startSearch(mSettings.getHomePage().equals(url) ? null : url, true,
createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), false);
// we do not need to remove it
removeTabFromContentView(currentTab);
}
- removeTabFromContentView(tab);
mTabControl.setCurrentTab(tab);
attachTabToContentView(tab);
+ resetTitleIconAndProgress();
+ updateLockIconToLatest();
return true;
}
+ /* package */ TabControl.Tab openTabToHomePage() {
+ return openTabAndShow(mSettings.getHomePage(), false, null);
+ }
+
/* package */ void closeCurrentWindow() {
final TabControl.Tab current = mTabControl.getCurrentTab();
if (mTabControl.getTabCount() == 1) {
- // This is the last tab. Open a new one, as well as the history
- // picker, and close the current one.
- TabControl.Tab newTab = openTabAndShow(
- BrowserActivity.EMPTY_URL_DATA, false, null);
- bookmarksOrHistoryPicker(false, true);
+ // This is the last tab. Open a new one, with the home
+ // page and close the current one.
+ TabControl.Tab newTab = openTabToHomePage();
closeTab(current);
- mTabControl.setCurrentTab(newTab);
return;
}
final TabControl.Tab parent = current.getParentTab();
}
}
+ private ActiveTabsPage mActiveTabsPage;
+
+ /**
+ * Remove the active tabs page.
+ * @param needToAttach If true, the active tabs page did not attach a tab
+ * to the content view, so we need to do that here.
+ */
+ /* package */ void removeActiveTabPage(boolean needToAttach) {
+ mContentView.removeView(mActiveTabsPage);
+ mActiveTabsPage = null;
+ mMenuState = R.id.MAIN_MENU;
+ if (needToAttach) {
+ attachTabToContentView(mTabControl.getCurrentTab());
+ }
+ getTopWindow().requestFocus();
+ }
+
@Override
public boolean onOptionsItemSelected(MenuItem item) {
if (!mCanChord) {
}
switch (item.getItemId()) {
// -- Main menu
+ case R.id.new_tab_menu_id:
+ openTabToHomePage();
+ break;
+
case R.id.goto_menu_id:
- bookmarksOrHistoryPicker(false, false);
+ onSearchRequested();
+ break;
+
+ case R.id.bookmarks_menu_id:
+ bookmarksOrHistoryPicker(false);
+ break;
+
+ case R.id.active_tabs_menu_id:
+ mActiveTabsPage = new ActiveTabsPage(this, mTabControl);
+ removeTabFromContentView(mTabControl.getCurrentTab());
+ hideFakeTitleBar();
+ mContentView.addView(mActiveTabsPage, COVER_SCREEN_PARAMS);
+ mActiveTabsPage.requestFocus();
+ mMenuState = EMPTY_MENU;
break;
case R.id.add_bookmark_menu_id:
WebView w = getTopWindow();
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.classic_history_menu_id:
- bookmarksOrHistoryPicker(true, false);
+ bookmarksOrHistoryPicker(true);
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:
if (desiredTab != null &&
desiredTab != mTabControl.getCurrentTab()) {
switchToTab(id);
- mTitleBar.setCurrentTab(id);
}
break;
}
menu.findItem(R.id.forward_menu_id)
.setEnabled(canGoForward);
+ menu.findItem(R.id.new_tab_menu_id).setEnabled(
+ mTabControl.getTabCount() < TabControl.MAX_TABS);
+
// decide whether to show the share link option
PackageManager pm = getPackageManager();
Intent send = new Intent(Intent.ACTION_SEND);
.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.
- mContentView.addView(t.getContainer(), COVER_SCREEN_PARAMS);
+ t.attachTabToContentView(mContentView);
if (mShouldShowErrorConsole) {
ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true);
ViewGroup.LayoutParams.WRAP_CONTENT));
}
- // Attach the sub window if necessary
- attachSubWindow(t);
+ 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(mTitleBar);
// Request focus on the top window.
t.getTopWindow().requestFocus();
}
// Attach a sub window to the main WebView of the given tab.
private void attachSubWindow(TabControl.Tab t) {
- // If a sub window exists, attach it to the content view.
- final WebView subView = t.getSubWebView();
- if (subView != null) {
- final View container = t.getSubWebViewContainer();
- mContentView.addView(container, COVER_SCREEN_PARAMS);
- subView.requestFocus();
- }
+ t.attachSubWindow(mContentView);
+ getTopWindow().requestFocus();
}
// Remove the given tab from the content view.
private void removeTabFromContentView(TabControl.Tab t) {
// Remove the container that contains the main WebView.
- mContentView.removeView(t.getContainer());
+ t.removeTabFromContentView(mContentView);
if (mTabControl.getCurrentErrorConsole(false) != null) {
mErrorConsoleContainer.removeView(mTabControl.getCurrentErrorConsole(false));
}
- // Remove the sub window if it exists.
- if (t.getSubWebView() != null) {
- mContentView.removeView(t.getSubWebViewContainer());
+ WebView view = t.getWebView();
+ if (view != null) {
+ 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;
+ }
}
}
// Remove the sub window if it exists. Also called by TabControl when the
// user clicks the 'X' to dismiss a sub window.
/* package */ void dismissSubWindow(TabControl.Tab t) {
- final WebView mainView = t.getWebView();
- if (t.getSubWebView() != null) {
- // Remove the container view and request focus on the main WebView.
- mContentView.removeView(t.getSubWebViewContainer());
- mainView.requestFocus();
- // Tell the TabControl to dismiss the subwindow. This will destroy
- // the WebView.
- mTabControl.dismissSubWindow(t);
- }
+ t.removeSubWindow(mContentView);
+ // Tell the TabControl to dismiss the subwindow. This will destroy
+ // the WebView.
+ mTabControl.dismissSubWindow(t);
+ getTopWindow().requestFocus();
}
// A wrapper function of {@link #openTabAndShow(UrlData, boolean, String)}
final TabControl.Tab tab = mTabControl.createNewTab(
closeOnExit, appId, urlData.mUrl);
WebView webview = tab.getWebView();
- if (CUSTOM_BROWSER_BAR) {
- mTitleBar.addTab(webview, true);
+ // If the last tab was removed from the active tabs page, currentTab
+ // will be null.
+ if (currentTab != null) {
+ removeTabFromContentView(currentTab);
}
- removeTabFromContentView(currentTab);
- attachTabToContentView(tab);
// We must set the new tab as the current tab to reflect the old
// animation behavior.
mTabControl.setCurrentTab(tab);
+ attachTabToContentView(tab);
if (!urlData.isEmpty()) {
urlData.loadIn(webview);
}
TabControl.Tab t = mTabControl.createNewTab();
if (t != null) {
WebView view = t.getWebView();
- if (CUSTOM_BROWSER_BAR) {
- mTitleBar.addTab(view, false);
- }
view.loadUrl(url);
}
return t;
}
/**
- * Resets the browser title-view to whatever it must be (for example, if we
- * load a page from history).
- */
- private void resetTitle() {
- resetLockIcon();
- resetTitleIconAndProgress();
- }
-
- /**
* Resets the browser title-view to whatever it must be
* (for example, if we had a loading error)
* When we have a new page, we call resetTitle, when we
private void resetTitleAndIcon(WebView view) {
WebHistoryItem item = view.copyBackForwardList().getCurrentItem();
if (item != null) {
- setUrlTitle(item.getUrl(), item.getTitle(), view);
+ setUrlTitle(item.getUrl(), item.getTitle());
setFavicon(item.getFavicon());
} else {
- setUrlTitle(null, null, view);
+ setUrlTitle(null, null);
setFavicon(null);
}
}
* @param url The URL of the site being loaded.
* @param title The title of the site being loaded.
*/
- private void setUrlTitle(String url, String title, WebView view) {
+ private void setUrlTitle(String url, String title) {
mUrl = url;
mTitle = title;
- if (CUSTOM_BROWSER_BAR) {
- mTitleBar.setTitleAndUrl(title, url, view);
- } else {
- setTitle(buildUrlTitle(url, title));
+ mTitleBar.setTitleAndUrl(title, url);
+ if (mFakeTitleBar != null) {
+ mFakeTitleBar.setTitleAndUrl(title, url);
}
}
/**
- * Builds and returns the page title, which is some
- * combination of the page URL and title.
- * @param url The URL of the site being loaded.
- * @param title The title of the site being loaded.
- * @return The page title.
- */
- private String buildUrlTitle(String url, String title) {
- String urlTitle = "";
-
- if (url != null) {
- String titleUrl = buildTitleUrl(url);
-
- if (title != null && 0 < title.length()) {
- if (titleUrl != null && 0 < titleUrl.length()) {
- urlTitle = titleUrl + ": " + title;
- } else {
- urlTitle = title;
- }
- } else {
- if (titleUrl != null) {
- urlTitle = titleUrl;
- }
- }
- }
-
- return urlTitle;
- }
-
- /**
* @param url The URL to build a title version of the URL from.
* @return The title version of the URL or null if fails.
* The title version of the URL can be either the URL hostname,
// Set the favicon in the title bar.
private void setFavicon(Bitmap icon) {
- if (CUSTOM_BROWSER_BAR) {
- Drawable[] array = new Drawable[3];
- array[0] = new PaintDrawable(Color.BLACK);
- PaintDrawable p = new PaintDrawable(Color.WHITE);
- array[1] = p;
- if (icon == null) {
- array[2] = mGenericFavicon;
- } else {
- array[2] = new BitmapDrawable(icon);
- }
- LayerDrawable d = new LayerDrawable(array);
- d.setLayerInset(1, 1, 1, 1, 1);
- d.setLayerInset(2, 2, 2, 2, 2);
- mTitleBar.setFavicon(d, getTopWindow());
- } else {
- Drawable[] array = new Drawable[2];
- PaintDrawable p = new PaintDrawable(Color.WHITE);
- p.setCornerRadius(3f);
- array[0] = p;
- if (icon == null) {
- array[1] = mGenericFavicon;
- } else {
- array[1] = new BitmapDrawable(icon);
- }
- LayerDrawable d = new LayerDrawable(array);
- d.setLayerInset(1, 2, 2, 2, 2);
- getWindow().setFeatureDrawable(Window.FEATURE_LEFT_ICON, d);
+ mTitleBar.setFavicon(icon);
+ if (mFakeTitleBar != null) {
+ mFakeTitleBar.setFavicon(icon);
}
}
" revert lock icon to " + mLockIconType);
}
- updateLockIconImage(mLockIconType);
+ updateLockIconToLatest();
}
/**
- * Close the tab after removing its associated title bar.
+ * Close the tab, remove its associated title bar, and adjust mTabControl's
+ * current tab to a valid value.
*/
- private void closeTab(TabControl.Tab t) {
- mTitleBar.removeTab(mTabControl.getTabIndex(t));
+ /* package */ void closeTab(TabControl.Tab t) {
+ int currentIndex = mTabControl.getCurrentIndex();
+ int removeIndex = mTabControl.getTabIndex(t);
mTabControl.removeTab(t);
+ if (currentIndex >= removeIndex && currentIndex != 0) {
+ currentIndex--;
+ }
+ mTabControl.setCurrentTab(mTabControl.getTab(currentIndex));
+ resetTitleIconAndProgress();
}
private void goBackOnePageOrQuit() {
* moveTaskToBack().
*/
moveTaskToBack(true);
+ return;
}
WebView w = current.getWebView();
if (w.canGoBack()) {
closeTab(current);
} else {
if (current.closeOnExit()) {
+ // force mPageStarted to be false as we are going to either
+ // finish the activity or remove the tab. This will ensure
+ // pauseWebView() taking action.
+ mPageStarted = false;
if (mTabControl.getTabCount() == 1) {
finish();
return;
}
// call pauseWebViewTimers() now, we won't be able to call
// it in onPause() as the WebView won't be valid.
+ // Temporarily change mActivityInPause to be true as
+ // pauseWebViewTimers() will do nothing if mActivityInPause
+ // is false.
+ boolean savedState = mActivityInPause;
+ if (savedState) {
+ Log.e(LOGTAG, "BrowserActivity is already paused "
+ + "while handing goBackOnePageOrQuit.");
+ }
+ mActivityInPause = true;
pauseWebViewTimers();
+ mActivityInPause = savedState;
removeTabFromContentView(current);
mTabControl.removeTab(current);
}
}
}
- 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, false);
- 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);
}
- private void stopLoading() {
+ /* 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);
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
resetLockIcon(url);
- setUrlTitle(url, null, view);
+ 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) {
// 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;
+ mDidStopLoad = false;
+ showFakeTitleBar();
updateInLoadMenuItems();
if (!mIsNetworkUp) {
- if ( mAlertDialog == null) {
- mAlertDialog = new AlertDialog.Builder(BrowserActivity.this)
- .setTitle(R.string.loadSuspendedTitle)
- .setMessage(R.string.loadSuspended)
- .setPositiveButton(R.string.ok, null)
- .show();
- }
+ createAndShowNetworkDialog();
if (view != null) {
view.setNetworkAvailable(false);
}
// 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
- updateLockIconImage(mLockIconType);
- updateScreenshot(view);
+ updateLockIconToLatest();
// Performance probe
if (false) {
}
}
ErrorDialog errDialog = new ErrorDialog(
- err == EventHandler.FILE_NOT_FOUND_ERROR ?
+ err == WebViewClient.ERROR_FILE_NOT_FOUND ?
R.string.browserFrameFileErrorLabel :
R.string.browserFrameNetworkErrorLabel,
desc, err);
@Override
public void onReceivedError(WebView view, int errorCode,
String description, String failingUrl) {
- if (errorCode != EventHandler.ERROR_LOOKUP &&
- errorCode != EventHandler.ERROR_CONNECT &&
- errorCode != EventHandler.ERROR_BAD_URL &&
- errorCode != EventHandler.ERROR_UNSUPPORTED_SCHEME &&
- errorCode != EventHandler.FILE_ERROR) {
+ if (errorCode != WebViewClient.ERROR_HOST_LOOKUP &&
+ errorCode != WebViewClient.ERROR_CONNECT &&
+ errorCode != WebViewClient.ERROR_BAD_URL &&
+ errorCode != WebViewClient.ERROR_UNSUPPORTED_SCHEME &&
+ errorCode != WebViewClient.ERROR_FILE) {
queueError(errorCode, description);
}
Log.e(LOGTAG, "onReceivedError " + errorCode + " " + failingUrl
if (url.regionMatches(true, 0, "about:", 0, 6)) {
return;
}
+ // remove "client" before updating it to the history so that it wont
+ // show up in the auto-complete list.
+ int index = url.indexOf("client=ms-");
+ if (index > 0 && url.contains(".google.")) {
+ int end = url.indexOf('&', index);
+ if (end > 0) {
+ url = url.substring(0, index-1).concat(url.substring(end));
+ } else {
+ url = url.substring(0, index-1);
+ }
+ }
Browser.updateVisitedHistory(mResolver, url, true);
WebIconDatabase.getInstance().retainIconForPageUrl(url);
}
}
@Override
- public void onChangeViewingMode(boolean toZoomedOut) {
- if (!CUSTOM_BROWSER_BAR) {
- return;
- }
- if (toZoomedOut) {
- // FIXME: animate the title bar into view
- mTitleBar.setVisibility(View.VISIBLE);
- } else {
- // FXIME: animate the title bar out of view
- mTitleBar.setVisibility(View.GONE);
- }
- }
-
- @Override
public boolean onCreateWindow(WebView view, final boolean dialog,
final boolean userGesture, final Message resultMsg) {
// Short-circuit if we can't create any more tabs or sub windows.
@Override
public void onProgressChanged(WebView view, int newProgress) {
- if (CUSTOM_BROWSER_BAR) {
- mTitleBar.setProgress(newProgress, view);
- } else {
- getWindow().setFeatureInt(Window.FEATURE_PROGRESS,
- newProgress * 100);
+ mTitleBar.setProgress(newProgress);
+ if (mFakeTitleBar != null) {
+ mFakeTitleBar.setProgress(newProgress);
}
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;
updateInLoadMenuItems();
+ // If the options menu is open, leave the title bar
+ if (!mOptionsMenuOpen || !mIconView) {
+ hideFakeTitleBar();
+ }
}
- } else {
+ } else if (!mInLoad) {
// onPageFinished may have already been called but a subframe
// is still loading and updating the progress. Reset mInLoad
// and update the menu items.
- if (!mInLoad) {
- mInLoad = true;
- updateInLoadMenuItems();
+ mInLoad = true;
+ updateInLoadMenuItems();
+ if (!mOptionsMenuOpen || mIconView) {
+ // This page has begun to load, so show the title bar
+ showFakeTitleBar();
}
}
}
String url = view.getUrl();
// here, if url is null, we want to reset the title
- setUrlTitle(url, title, view);
+ setUrlTitle(url, title);
if (url == null ||
url.length() >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) {
}
@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();
}
* @param databaseIdentifier the identifier of the database on
* which the transaction that caused the quota overflow was run
* @param currentQuota the current quota for the origin.
+ * @param estimatedSize the estimated size of the database.
* @param totalUsedQuota is the sum of all origins' quota.
* @param quotaUpdater The callback to run when a decision to allow or
* deny quota has been made. Don't forget to call this!
*/
@Override
public void onExceededDatabaseQuota(String url,
- String databaseIdentifier, long currentQuota, long totalUsedQuota,
- WebStorage.QuotaUpdater quotaUpdater) {
+ String databaseIdentifier, long currentQuota, long estimatedSize,
+ long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
mSettings.getWebStorageSizeManager().onExceededDatabaseQuota(
- url, databaseIdentifier, currentQuota, totalUsedQuota,
- quotaUpdater);
+ url, databaseIdentifier, currentQuota, estimatedSize,
+ totalUsedQuota, quotaUpdater);
}
/**
}
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;
+ }
};
/**
// if we're dealing wih A/V content that's not explicitly marked
// for download, check if it's streamable.
if (contentDisposition == null
- || !contentDisposition.regionMatches(true, 0, "attachment", 0, 10)) {
+ || !contentDisposition.regionMatches(
+ true, 0, "attachment", 0, 10)) {
// query the package manager to see if there's a registered handler
// that matches.
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setDataAndType(Uri.parse(url), mimetype);
- if (getPackageManager().resolveActivity(intent,
- PackageManager.MATCH_DEFAULT_ONLY) != null) {
- // someone knows how to handle this mime type with this scheme, don't download.
- try {
- startActivity(intent);
- return;
- } catch (ActivityNotFoundException ex) {
- if (LOGD_ENABLED) {
- Log.d(LOGTAG, "activity not found for " + mimetype
- + " over " + Uri.parse(url).getScheme(), ex);
+ ResolveInfo info = getPackageManager().resolveActivity(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ if (info != null) {
+ ComponentName myName = getComponentName();
+ // If we resolved to ourselves, we don't want to attempt to
+ // load the url only to try and download it again.
+ if (!myName.getPackageName().equals(
+ info.activityInfo.packageName)
+ || !myName.getClassName().equals(
+ info.activityInfo.name)) {
+ // someone (other than us) knows how to handle this mime
+ // type with this scheme, don't download.
+ try {
+ startActivity(intent);
+ return;
+ } catch (ActivityNotFoundException ex) {
+ if (LOGD_ENABLED) {
+ Log.d(LOGTAG, "activity not found for " + mimetype
+ + " over " + Uri.parse(url).getScheme(),
+ ex);
+ }
+ // Best behavior is to fall back to a download in this
+ // case
}
- // Best behavior is to fall back to a download in this case
}
}
}
updateLockIconImage(LOCK_ICON_UNSECURE);
}
- /**
- * Resets the lock icon. This method is called when the icon needs to be
- * reset but we do not know whether we are loading a secure or not secure
- * page.
- */
- private void resetLockIcon() {
- // Save the lock-icon state (we revert to it if the load gets cancelled)
- saveLockIcon();
+ /* package */ void setLockIconType(int type) {
+ mLockIconType = type;
+ }
- mLockIconType = LOCK_ICON_UNSECURE;
+ /* package */ int getLockIconType() {
+ return mLockIconType;
+ }
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
- " reset lock icon to " + mLockIconType);
- }
+ /* package */ void setPrevLockType(int type) {
+ mPrevLockType = type;
+ }
- updateLockIconImage(LOCK_ICON_UNSECURE);
+ /* package */ int getPrevLockType() {
+ return mPrevLockType;
+ }
+
+ /**
+ * Update the lock icon to correspond to our latest state.
+ */
+ /* package */ void updateLockIconToLatest() {
+ updateLockIconImage(mLockIconType);
}
/**
} else if (lockIconType == LOCK_ICON_MIXED) {
d = mMixLockIcon;
}
- if (CUSTOM_BROWSER_BAR) {
- mTitleBar.setLock(d, getTopWindow());
- } else {
- getWindow().setFeatureDrawable(Window.FEATURE_RIGHT_ICON, d);
+ mTitleBar.setLock(d);
+ if (mFakeTitleBar != null) {
+ mFakeTitleBar.setLock(d);
}
}
}
} else {
mIsNetworkUp = false;
- if (mInLoad && mAlertDialog == null) {
- mAlertDialog = new AlertDialog.Builder(this)
- .setTitle(R.string.loadSuspendedTitle)
- .setMessage(R.string.loadSuspended)
- .setPositiveButton(R.string.ok, null)
- .show();
- }
+ if (mInLoad) {
+ createAndShowNetworkDialog();
+ }
}
WebView w = mTabControl.getCurrentWebView();
if (w != null) {
}
}
+ // This method shows the network dialog alerting the user that the net is
+ // down. It will only show the dialog if mAlertDialog is null.
+ private void createAndShowNetworkDialog() {
+ if (mAlertDialog == null) {
+ mAlertDialog = new AlertDialog.Builder(this)
+ .setTitle(R.string.loadSuspendedTitle)
+ .setMessage(R.string.loadSuspended)
+ .setPositiveButton(R.string.ok, null)
+ .show();
+ }
+ }
+
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
String data = intent.getAction();
Bundle extras = intent.getExtras();
if (extras != null && extras.getBoolean("new_window", false)) {
- final TabControl.Tab newTab = openTab(data);
- if (mSettings.openInBackground() &&
- newTab != null) {
- mTabControl.populatePickerData(newTab);
- mTabControl.setCurrentTab(newTab);
- int newIndex = mTabControl.getCurrentIndex();
- if (CUSTOM_BROWSER_BAR) {
- mTitleBar.setCurrentTab(newIndex);
- }
- }
+ openTab(data);
} else {
final TabControl.Tab currentTab =
mTabControl.getCurrentTab();
getTopWindow().loadUrl(data);
}
}
- } else if (resultCode == RESULT_CANCELED
- && mCancelGoPageMeansClose) {
- if (mTabControl.getTabCount() == 1) {
- // finish the Browser. When the Browser opens up again,
- // we will go through onCreate and once again open up
- // the Go page.
- finish();
- return;
- }
- closeCurrentWindow();
}
break;
default:
break;
}
- mCancelGoPageMeansClose = false;
- if (getTopWindow() != null) {
- getTopWindow().requestFocus();
- }
+ getTopWindow().requestFocus();
}
/*
}
- // True if canceling the "Go" screen should result in closing the current
- // window/browser.
- private boolean mCancelGoPageMeansClose;
-
/**
* Open the Go page.
* @param startWithHistory If true, open starting on the history tab.
* Otherwise, start with the bookmarks tab.
- * @param cancelGoPageMeansClose Set to true if this came from a new tab, or
- * from the only tab, and canceling means to
- * close the tab (and possibly the browser)
*/
- /* package */ void bookmarksOrHistoryPicker(boolean startWithHistory,
- boolean cancelGoPageMeansClose) {
+ /* package */ void bookmarksOrHistoryPicker(boolean startWithHistory) {
WebView current = mTabControl.getCurrentWebView();
if (current == null) {
return;
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);
- // If this is opening in a new window, then disable opening in a
- // (different) new window. Also disable it if we have maxed out the
- // windows.
- intent.putExtra("disable_new_window", cancelGoPageMeansClose
- || mTabControl.getTabCount() >= TabControl.MAX_TABS);
+ 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);
intent.putExtra("touch_icon_url", current.getTouchIconUrl());
if (startWithHistory) {
intent.putExtra(CombinedBookmarkHistoryActivity.STARTING_TAB,
CombinedBookmarkHistoryActivity.HISTORY_TAB);
}
- mCancelGoPageMeansClose = cancelGoPageMeansClose;
startActivityForResult(intent, COMBO_PAGE);
}
}
- private final static int LOCK_ICON_UNSECURE = 0;
- private final static int LOCK_ICON_SECURE = 1;
- private final static int LOCK_ICON_MIXED = 2;
+ final static int LOCK_ICON_UNSECURE = 0;
+ final static int LOCK_ICON_SECURE = 1;
+ final static int LOCK_ICON_MIXED = 2;
private int mLockIconType = LOCK_ICON_UNSECURE;
private int mPrevLockType = LOCK_ICON_UNSECURE;
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 Drawable mMixLockIcon;
private Drawable mSecLockIcon;
- private Drawable mGenericFavicon;
/* hold a ref so we can auto-cancel if necessary */
private AlertDialog mAlertDialog;
private Toast mStopToast;
- private TitleBarSet mTitleBar;
+ private TitleBar mTitleBar;
private LinearLayout mErrorConsoleContainer = null;
private boolean mShouldShowErrorConsole = false;
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.