package com.android.browser;
-import com.google.android.googleapps.IGoogleLoginService;
-import com.google.android.googlelogin.GoogleLoginServiceConstants;
-
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.ActivityNotFoundException;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentProvider;
+import android.content.ContentProviderClient;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.content.DialogInterface.OnCancelListener;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
-import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.Cursor;
-import android.database.sqlite.SQLiteDatabase;
-import android.database.sqlite.SQLiteException;
+import android.database.DatabaseUtils;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
-import android.graphics.DrawFilter;
-import android.graphics.Paint;
-import android.graphics.PaintFlagsDrawFilter;
import android.graphics.Picture;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
-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.net.http.SslCertificate;
import android.net.http.SslError;
import android.os.AsyncTask;
import android.os.Debug;
import android.os.Environment;
import android.os.Handler;
-import android.os.IBinder;
import android.os.Message;
import android.os.PowerManager;
import android.os.Process;
-import android.os.RemoteException;
import android.os.ServiceManager;
import android.os.SystemClock;
import android.provider.Browser;
import android.provider.ContactsContract.Intents.Insert;
import android.provider.Downloads;
import android.provider.MediaStore;
+import android.speech.RecognizerResultsIntent;
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.util.Patterns;
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.WindowManager;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MenuItem.OnMenuItemClickListener;
-import android.view.animation.AlphaAnimation;
-import android.view.animation.Animation;
-import android.view.animation.AnimationSet;
-import android.view.animation.DecelerateInterpolator;
-import android.view.animation.ScaleAnimation;
-import android.view.animation.TranslateAnimation;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.DownloadListener;
-import android.webkit.GeolocationPermissions;
import android.webkit.HttpAuthHandler;
import android.webkit.PluginManager;
import android.webkit.SslErrorHandler;
import android.webkit.URLUtil;
import android.webkit.ValueCallback;
import android.webkit.WebChromeClient;
-import android.webkit.WebChromeClient.CustomViewCallback;
import android.webkit.WebHistoryItem;
import android.webkit.WebIconDatabase;
-import android.webkit.WebStorage;
import android.webkit.WebView;
-import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
+import android.accounts.Account;
+import android.accounts.AccountManager;
+import android.accounts.AccountManagerFuture;
+import android.accounts.AuthenticatorException;
+import android.accounts.OperationCanceledException;
+import android.accounts.AccountManagerCallback;
+
+import com.android.common.Search;
+import com.android.common.speech.LoggingEvents;
-import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URLEncoder;
import java.text.ParseException;
import java.util.Date;
-import java.util.Enumeration;
import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.Vector;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
-import java.util.zip.ZipEntry;
-import java.util.zip.ZipFile;
public class BrowserActivity extends Activity
- implements View.OnCreateContextMenuListener,
- DownloadListener {
+ implements View.OnCreateContextMenuListener, DownloadListener {
/* Define some aliases to make these debugging flags easier to refer to.
* This file imports android.provider.Browser, so we can't just refer to "Browser.DEBUG".
private final static boolean LOGV_ENABLED = com.android.browser.Browser.LOGV_ENABLED;
private final static boolean LOGD_ENABLED = com.android.browser.Browser.LOGD_ENABLED;
- private IGoogleLoginService mGls = null;
- private ServiceConnection mGlsConnection = null;
-
- private SensorManager mSensorManager = null;
-
// These are single-character shortcuts for searching popular sources.
private static final int SHORTCUT_INVALID = 0;
private static final int SHORTCUT_GOOGLE_SEARCH = 1;
private static final int SHORTCUT_DICTIONARY_SEARCH = 3;
private static final int SHORTCUT_GOOGLE_MOBILE_LOCAL_SEARCH = 4;
- /* Whitelisted webpages
- private static HashSet<String> sWhiteList;
-
- static {
- sWhiteList = new HashSet<String>();
- sWhiteList.add("cnn.com/");
- sWhiteList.add("espn.go.com/");
- sWhiteList.add("nytimes.com/");
- sWhiteList.add("engadget.com/");
- sWhiteList.add("yahoo.com/");
- sWhiteList.add("msn.com/");
- sWhiteList.add("amazon.com/");
- sWhiteList.add("consumerist.com/");
- sWhiteList.add("google.com/m/news");
- }
- */
-
- private void setupHomePage() {
- final Runnable getAccount = new Runnable() {
- public void run() {
- // Lower priority
- Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
- // get the default home page
- String homepage = mSettings.getHomePage();
-
- try {
- if (mGls == null) return;
-
- if (!homepage.startsWith("http://www.google.")) return;
- if (homepage.indexOf('?') == -1) return;
-
- String hostedUser = mGls.getAccount(GoogleLoginServiceConstants.PREFER_HOSTED);
- String googleUser = mGls.getAccount(GoogleLoginServiceConstants.REQUIRE_GOOGLE);
-
- // three cases:
- //
- // hostedUser == googleUser
- // The device has only a google account
- //
- // hostedUser != googleUser
- // The device has a hosted account and a google account
- //
- // hostedUser != null, googleUser == null
- // The device has only a hosted account (so far)
-
- // developers might have no accounts at all
- if (hostedUser == null) return;
-
- if (googleUser == null || !hostedUser.equals(googleUser)) {
- String domain = hostedUser.substring(hostedUser.lastIndexOf('@')+1);
- homepage = homepage.replace("?", "/a/" + domain + "?");
- }
- } catch (RemoteException ignore) {
- // Login service died; carry on
- } catch (RuntimeException ignore) {
- // Login service died; carry on
- } finally {
- finish(homepage);
- }
- }
-
- private void finish(final String homepage) {
- mHandler.post(new Runnable() {
- public void run() {
- mSettings.setHomePage(BrowserActivity.this, homepage);
- resumeAfterCredentials();
-
- // as this is running in a separate thread,
- // BrowserActivity's onDestroy() may have been called,
- // which also calls unbindService().
- if (mGlsConnection != null) {
- // we no longer need to keep GLS open
- unbindService(mGlsConnection);
- mGlsConnection = null;
- }
- } });
- } };
-
- final boolean[] done = { false };
-
- // Open a connection to the Google Login Service. The first
- // time the connection is established, set up the homepage depending on
- // the account in a background thread.
- mGlsConnection = new ServiceConnection() {
- public void onServiceConnected(ComponentName className, IBinder service) {
- mGls = IGoogleLoginService.Stub.asInterface(service);
- if (done[0] == false) {
- done[0] = true;
- Thread account = new Thread(getAccount);
- account.setName("GLSAccount");
- account.start();
- }
- }
- public void onServiceDisconnected(ComponentName className) {
- mGls = null;
- }
- };
-
- bindService(GoogleLoginServiceConstants.SERVICE_INTENT,
- mGlsConnection, Context.BIND_AUTO_CREATE);
- }
-
private static class ClearThumbnails extends AsyncTask<File, Void, Void> {
@Override
public Void doInBackground(File... files) {
*/
private FrameLayout mBrowserFrameLayout;
- @Override public void onCreate(Bundle icicle) {
+ @Override
+ public void onCreate(Bundle icicle) {
if (LOGV_ENABLED) {
Log.v(LOGTAG, this + " onStart");
}
// test the browser in OpenGL
// requestWindowFeature(Window.FEATURE_OPENGL);
+ // enable this to test the browser in 32bit
+ if (false) {
+ getWindow().setFormat(PixelFormat.RGBX_8888);
+ BitmapFactory.setDefaultConfig(Bitmap.Config.ARGB_8888);
+ }
+
setDefaultKeyMode(DEFAULT_KEYS_SEARCH_LOCAL);
mResolver = getContentResolver();
return;
}
- //
- // start MASF proxy service
- //
- //Intent proxyServiceIntent = new Intent();
- //proxyServiceIntent.setComponent
- // (new ComponentName(
- // "com.android.masfproxyservice",
- // "com.android.masfproxyservice.MasfProxyService"));
- //startService(proxyServiceIntent, null);
-
mSecLockIcon = Resources.getSystem().getDrawable(
android.R.drawable.ic_secure);
mMixLockIcon = Resources.getSystem().getDrawable(
.findViewById(R.id.fullscreen_custom_content);
frameLayout.addView(mBrowserFrameLayout, COVER_SCREEN_PARAMS);
mTitleBar = new TitleBar(this);
+ // mTitleBar will be always shown in the fully loaded mode
+ mTitleBar.setProgress(100);
+ mFakeTitleBar = new TitleBar(this);
// Create the tab control and our initial tab
mTabControl = new TabControl(this);
// Keep a settings instance handy.
mSettings = BrowserSettings.getInstance();
mSettings.setTabControl(mTabControl);
- mSettings.loadFromDb(this);
PowerManager pm = (PowerManager) getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Browser");
+ // Find out if the network is currently up.
+ ConnectivityManager cm = (ConnectivityManager) getSystemService(
+ Context.CONNECTIVITY_SERVICE);
+ NetworkInfo info = cm.getActiveNetworkInfo();
+ if (info != null) {
+ mIsNetworkUp = info.isAvailable();
+ }
+
/* enables registration for changes in network status from
http stack */
mNetworkStateChangedFilter = new IntentFilter();
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(
ConnectivityManager.CONNECTIVITY_ACTION)) {
- boolean noConnectivity = intent.getBooleanExtra(
- ConnectivityManager.EXTRA_NO_CONNECTIVITY, false);
- onNetworkToggle(!noConnectivity);
+
+ NetworkInfo info = intent.getParcelableExtra(
+ ConnectivityManager.EXTRA_NETWORK_INFO);
+ String typeName = info.getTypeName();
+ String subtypeName = info.getSubtypeName();
+ sendNetworkType(typeName.toLowerCase(),
+ (subtypeName != null ? subtypeName.toLowerCase() : ""));
+
+ onNetworkToggle(info.isAvailable());
}
}
};
// if it is replacing, refreshPlugins() when adding
return;
}
+
+ if (sGoogleApps.contains(packageName)) {
+ BrowserActivity.this.packageChanged(packageName,
+ Intent.ACTION_PACKAGE_ADDED.equals(action));
+ }
+
PackageManager pm = BrowserActivity.this.getPackageManager();
PackageInfo pkgInfo = null;
try {
// the tab will be close when exit.
UrlData urlData = getUrlDataFromIntent(intent);
- final TabControl.Tab t = mTabControl.createNewTab(
- Intent.ACTION_VIEW.equals(intent.getAction()) &&
- intent.getData() != null,
+ String action = intent.getAction();
+ final Tab t = mTabControl.createNewTab(
+ (Intent.ACTION_VIEW.equals(action) &&
+ intent.getData() != null)
+ || RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS
+ .equals(action),
intent.getStringExtra(Browser.EXTRA_APPLICATION_ID), urlData.mUrl);
mTabControl.setCurrentTab(t);
attachTabToContentView(t);
webView.setInitialScale(scale);
}
}
- // If we are not restoring from an icicle, then there is a high
- // likely hood this is the first run. So, check to see if the
- // homepage needs to be configured and copy any plugins from our
- // asset directory to the data partition.
- if ((extra == null || !extra.getBoolean("testing"))
- && !mSettings.isLoginInitialized()) {
- setupHomePage();
- }
if (urlData.isEmpty()) {
- if (mSettings.isLoginInitialized()) {
- webView.loadUrl(mSettings.getHomePage());
- } else {
- waitForCredentials();
- }
+ loadUrl(webView, mSettings.getHomePage());
} else {
- if (extra != null) {
- urlData.setPostData(extra
- .getByteArray(Browser.EXTRA_POST_DATA));
- }
- urlData.loadIn(webView);
+ loadUrlDataIn(t, urlData);
}
} else {
// TabControl.restoreState() will create a new tab even if
if (jsFlags.trim().length() != 0) {
mTabControl.getCurrentWebView().setJsFlags(jsFlags);
}
+ // Work out which packages are installed on the system.
+ getInstalledPackages();
+
+ // Start watching the default geolocation permissions
+ mSystemAllowGeolocationOrigins
+ = new SystemAllowGeolocationOrigins(getApplicationContext());
+ mSystemAllowGeolocationOrigins.start();
+ }
+
+ /**
+ * Feed the previously stored results strings to the BrowserProvider so that
+ * the SearchDialog will show them instead of the standard searches.
+ * @param result String to show on the editable line of the SearchDialog.
+ */
+ /* package */ void showVoiceSearchResults(String result) {
+ ContentProviderClient client = mResolver.acquireContentProviderClient(
+ Browser.BOOKMARKS_URI);
+ ContentProvider prov = client.getLocalContentProvider();
+ BrowserProvider bp = (BrowserProvider) prov;
+ bp.setQueryResults(mTabControl.getCurrentTab().getVoiceSearchResults());
+ client.release();
+
+ Bundle bundle = createGoogleSearchSourceBundle(
+ GOOGLE_SEARCH_SOURCE_SEARCHKEY);
+ bundle.putBoolean(SearchManager.CONTEXT_IS_VOICE, true);
+ startSearch(result, false, bundle, false);
}
@Override
protected void onNewIntent(Intent intent) {
- TabControl.Tab current = mTabControl.getCurrentTab();
+ Tab current = mTabControl.getCurrentTab();
// When a tab is closed on exit, the current tab index is set to -1.
// Reset before proceed as Browser requires the current tab to be set.
if (current == null) {
// just resume the browser
return;
}
+ // In case the SearchDialog is open.
+ ((SearchManager) getSystemService(Context.SEARCH_SERVICE))
+ .stopSearch();
+ boolean activateVoiceSearch = RecognizerResultsIntent
+ .ACTION_VOICE_SEARCH_RESULTS.equals(action);
if (Intent.ACTION_VIEW.equals(action)
|| Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
- || Intent.ACTION_WEB_SEARCH.equals(action)) {
+ || Intent.ACTION_WEB_SEARCH.equals(action)
+ || activateVoiceSearch) {
+ if (current.isInVoiceSearchMode()) {
+ String title = current.getVoiceDisplayTitle();
+ if (title != null && title.equals(intent.getStringExtra(
+ SearchManager.QUERY))) {
+ // The user submitted the same search as the last voice
+ // search, so do nothing.
+ return;
+ }
+ if (Intent.ACTION_SEARCH.equals(action)
+ && current.voiceSearchSourceIsGoogle()) {
+ Intent logIntent = new Intent(
+ LoggingEvents.ACTION_LOG_EVENT);
+ logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
+ LoggingEvents.VoiceSearch.QUERY_UPDATED);
+ logIntent.putExtra(
+ LoggingEvents.VoiceSearch.EXTRA_QUERY_UPDATED_VALUE,
+ intent.getDataString());
+ sendBroadcast(logIntent);
+ // Note, onPageStarted will revert the voice title bar
+ // When http://b/issue?id=2379215 is fixed, we should update
+ // the title bar here.
+ }
+ }
// If this was a search request (e.g. search query directly typed into the address bar),
// pass it on to the default web search provider.
if (handleWebSearchIntent(intent)) {
if (urlData.isEmpty()) {
urlData = new UrlData(mSettings.getHomePage());
}
- urlData.setPostData(intent
- .getByteArrayExtra(Browser.EXTRA_POST_DATA));
final String appId = intent
.getStringExtra(Browser.EXTRA_APPLICATION_ID);
- if (Intent.ACTION_VIEW.equals(action)
+ if ((Intent.ACTION_VIEW.equals(action)
+ // If a voice search has no appId, it means that it came
+ // from the browser. In that case, reuse the current tab.
+ || (activateVoiceSearch && appId != null))
&& !getPackageName().equals(appId)
&& (flags & Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT) != 0) {
- TabControl.Tab appTab = mTabControl.getTabFromId(appId);
+ Tab appTab = mTabControl.getTabFromId(appId);
if (appTab != null) {
Log.i(LOGTAG, "Reusing tab for " + appId);
// Dismiss the subwindow if applicable.
// If the WebView has the same original url and is on that
// page, it can be reused.
boolean needsLoad =
- mTabControl.recreateWebView(appTab, urlData.mUrl);
+ mTabControl.recreateWebView(appTab, urlData);
if (current != appTab) {
switchToTab(mTabControl.getTabIndex(appTab));
if (needsLoad) {
- urlData.loadIn(appTab.getWebView());
+ loadUrlDataIn(appTab, urlData);
}
} else {
// If the tab was the current tab, we have to attach
// it to the view system again.
attachTabToContentView(appTab);
if (needsLoad) {
- urlData.loadIn(appTab.getWebView());
+ loadUrlDataIn(appTab, urlData);
}
}
return;
}
}
} else {
- if ("about:debug".equals(urlData.mUrl)) {
- mSettings.toggleDebugSettings();
+ if (!urlData.isEmpty()
+ && urlData.mUrl.startsWith("about:debug")) {
+ if ("about:debug.dom".equals(urlData.mUrl)) {
+ current.getWebView().dumpDomTree(false);
+ } else if ("about:debug.dom.file".equals(urlData.mUrl)) {
+ current.getWebView().dumpDomTree(true);
+ } else if ("about:debug.render".equals(urlData.mUrl)) {
+ current.getWebView().dumpRenderTree(false);
+ } else if ("about:debug.render.file".equals(urlData.mUrl)) {
+ current.getWebView().dumpRenderTree(true);
+ } else if ("about:debug.display".equals(urlData.mUrl)) {
+ current.getWebView().dumpDisplayTree();
+ } else if (urlData.mUrl.startsWith("about:debug.drag")) {
+ int index = urlData.mUrl.codePointAt(16) - '0';
+ if (index <= 0 || index > 9) {
+ current.getWebView().setDragTracker(null);
+ } else {
+ current.getWebView().setDragTracker(new MeshTracker(index));
+ }
+ } else {
+ mSettings.toggleDebugSettings();
+ }
return;
}
// Get rid of the subwindow if it exists
dismissSubWindow(current);
- urlData.loadIn(current.getWebView());
+ loadUrlDataIn(current, urlData);
}
}
}
String url = null;
final String action = intent.getAction();
+ if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS.equals(
+ action)) {
+ return false;
+ }
if (Intent.ACTION_VIEW.equals(action)) {
Uri data = intent.getData();
if (data != null) url = data.toString();
// URLs and site specific search shortcuts are handled by the regular flow of control, so
// return early.
- if (Regex.WEB_URL_PATTERN.matcher(url).matches()
+ if (Patterns.WEB_URL.matcher(url).matches()
|| ACCEPTED_URI_SCHEMA.matcher(url).matches()
|| parseUrlShortcut(url) != SHORTCUT_INVALID) {
return false;
}
- Browser.updateVisitedHistory(mResolver, url, false);
- Browser.addSearchUrl(mResolver, url);
+ final ContentResolver cr = mResolver;
+ final String newUrl = url;
+ new AsyncTask<Void, Void, Void>() {
+ protected Void doInBackground(Void... unused) {
+ Browser.updateVisitedHistory(cr, newUrl, false);
+ Browser.addSearchUrl(cr, newUrl);
+ return null;
+ }
+ }.execute();
Intent intent = new Intent(Intent.ACTION_WEB_SEARCH);
intent.addCategory(Intent.CATEGORY_DEFAULT);
}
private UrlData getUrlDataFromIntent(Intent intent) {
- String url = null;
+ String url = "";
+ Map<String, String> headers = null;
if (intent != null) {
final String action = intent.getAction();
if (Intent.ACTION_VIEW.equals(action)) {
url += "?" + mimeType;
}
}
- if ("inline:".equals(url)) {
- return new InlinedUrlData(
- intent.getStringExtra(Browser.EXTRA_INLINE_CONTENT),
- intent.getType(),
- intent.getStringExtra(Browser.EXTRA_INLINE_ENCODING),
- intent.getStringExtra(Browser.EXTRA_INLINE_FAILURL));
+ if (url != null && url.startsWith("http")) {
+ final Bundle pairs = intent
+ .getBundleExtra(Browser.EXTRA_HEADERS);
+ if (pairs != null && !pairs.isEmpty()) {
+ Iterator<String> iter = pairs.keySet().iterator();
+ headers = new HashMap<String, String>();
+ while (iter.hasNext()) {
+ String key = iter.next();
+ headers.put(key, pairs.getString(key));
+ }
+ }
}
} else if (Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
url = intent.getStringExtra(SearchManager.QUERY);
if (url != null) {
mLastEnteredUrl = url;
- // Don't add Urls, just search terms.
- // Urls will get added when the page is loaded.
- if (!Regex.WEB_URL_PATTERN.matcher(url).matches()) {
- Browser.updateVisitedHistory(mResolver, url, false);
- }
// In general, we shouldn't modify URL from Intent.
// But currently, we get the user-typed URL from search box as well.
url = fixUrl(url);
url = smartUrlFilter(url);
+ final ContentResolver cr = mResolver;
+ final String newUrl = url;
+ new AsyncTask<Void, Void, Void>() {
+ protected Void doInBackground(Void... unused) {
+ Browser.updateVisitedHistory(cr, newUrl, false);
+ return null;
+ }
+ }.execute();
String searchSource = "&source=android-" + GOOGLE_SEARCH_SOURCE_SUGGEST + "&";
if (url.contains(searchSource)) {
String source = null;
final Bundle appData = intent.getBundleExtra(SearchManager.APP_DATA);
if (appData != null) {
- source = appData.getString(SearchManager.SOURCE);
+ source = appData.getString(Search.SOURCE);
}
if (TextUtils.isEmpty(source)) {
source = GOOGLE_SEARCH_SOURCE_UNKNOWN;
}
}
}
- return new UrlData(url);
+ return new UrlData(url, headers, intent);
}
+ /* package */ void showVoiceTitleBar(String title) {
+ mTitleBar.setInVoiceMode(true);
+ mFakeTitleBar.setInVoiceMode(true);
+ mTitleBar.setDisplayTitle(title);
+ mFakeTitleBar.setDisplayTitle(title);
+ }
+ /* package */ void revertVoiceTitleBar() {
+ mTitleBar.setInVoiceMode(false);
+ mFakeTitleBar.setInVoiceMode(false);
+
+ mTitleBar.setDisplayTitle(mUrl);
+ mFakeTitleBar.setDisplayTitle(mUrl);
+ }
/* package */ static String fixUrl(String inUrl) {
// FIXME: Converting the url to lower case
// duplicates functionality in smartUrlFilter().
return inUrl;
}
- /**
- * Looking for the pattern like this
- *
- * *
- * * *
- * *** * *******
- * * *
- * * *
- * *
- */
- private final SensorListener mSensorListener = new SensorListener() {
- private long mLastGestureTime;
- private float[] mPrev = new float[3];
- private float[] mPrevDiff = new float[3];
- private float[] mDiff = new float[3];
- private float[] mRevertDiff = new float[3];
-
- public void onSensorChanged(int sensor, float[] values) {
- boolean show = false;
- float[] diff = new float[3];
-
- for (int i = 0; i < 3; i++) {
- diff[i] = values[i] - mPrev[i];
- if (Math.abs(diff[i]) > 1) {
- show = true;
- }
- if ((diff[i] > 1.0 && mDiff[i] < 0.2)
- || (diff[i] < -1.0 && mDiff[i] > -0.2)) {
- // start track when there is a big move, or revert
- mRevertDiff[i] = mDiff[i];
- mDiff[i] = 0;
- } else if (diff[i] > -0.2 && diff[i] < 0.2) {
- // reset when it is flat
- mDiff[i] = mRevertDiff[i] = 0;
- }
- mDiff[i] += diff[i];
- mPrevDiff[i] = diff[i];
- mPrev[i] = values[i];
- }
-
- if (false) {
- // only shows if we think the delta is big enough, in an attempt
- // to detect "serious" moves left/right or up/down
- Log.d("BrowserSensorHack", "sensorChanged " + sensor + " ("
- + values[0] + ", " + values[1] + ", " + values[2] + ")"
- + " diff(" + diff[0] + " " + diff[1] + " " + diff[2]
- + ")");
- Log.d("BrowserSensorHack", " mDiff(" + mDiff[0] + " "
- + mDiff[1] + " " + mDiff[2] + ")" + " mRevertDiff("
- + mRevertDiff[0] + " " + mRevertDiff[1] + " "
- + mRevertDiff[2] + ")");
- }
-
- long now = android.os.SystemClock.uptimeMillis();
- if (now - mLastGestureTime > 1000) {
- mLastGestureTime = 0;
-
- float y = mDiff[1];
- float z = mDiff[2];
- float ay = Math.abs(y);
- float az = Math.abs(z);
- float ry = mRevertDiff[1];
- float rz = mRevertDiff[2];
- float ary = Math.abs(ry);
- float arz = Math.abs(rz);
- boolean gestY = ay > 2.5f && ary > 1.0f && ay > ary;
- boolean gestZ = az > 3.5f && arz > 1.0f && az > arz;
-
- if ((gestY || gestZ) && !(gestY && gestZ)) {
- WebView view = mTabControl.getCurrentWebView();
-
- if (view != null) {
- if (gestZ) {
- if (z < 0) {
- view.zoomOut();
- } else {
- view.zoomIn();
- }
- } else {
- view.flingScroll(0, Math.round(y * 100));
- }
- }
- mLastGestureTime = now;
- }
- }
- }
-
- public void onAccuracyChanged(int sensor, int accuracy) {
- // TODO Auto-generated method stub
-
- }
- };
-
- @Override protected void onResume() {
+ @Override
+ protected void onResume() {
super.onResume();
if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.onResume: this=" + this);
mWakeLock.release();
}
- if (mCredsDlg != null) {
- if (!mHandler.hasMessages(CANCEL_CREDS_REQUEST)) {
- // In case credential request never comes back
- mHandler.sendEmptyMessageDelayed(CANCEL_CREDS_REQUEST, 6000);
- }
- }
-
registerReceiver(mNetworkStateIntentReceiver,
mNetworkStateChangedFilter);
WebView.enablePlatformNotifications();
-
- if (mSettings.doFlick()) {
- if (mSensorManager == null) {
- mSensorManager = (SensorManager) getSystemService(
- Context.SENSOR_SERVICE);
- }
- mSensorManager.registerListener(mSensorListener,
- SensorManager.SENSOR_ACCELEROMETER,
- SensorManager.SENSOR_DELAY_FASTEST);
- } else {
- mSensorManager = null;
- }
}
/**
* 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.
+ * would change its appearance, use a different TitleBar to show overlayed
+ * at the top of the screen, when the menu is open or the page is loading.
*/
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.
*/
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");
- }
+ if (mFakeTitleBar.getParent() == null && mActiveTabsPage == null
+ && !mActivityInPause) {
+ WebView mainView = mTabControl.getCurrentWebView();
+ // if there is no current WebView, don't show the faked title bar;
+ if (mainView == null) {
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);
// while the menu is up
WindowManager.LayoutParams params
= new WindowManager.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT,
- WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL,
+ WindowManager.LayoutParams.TYPE_APPLICATION,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
PixelFormat.TRANSLUCENT);
params.gravity = Gravity.TOP;
- WebView mainView = mTabControl.getCurrentWebView();
- boolean atTop = mainView != null && mainView.getScrollY() == 0;
+ boolean atTop = mainView.getScrollY() == 0;
params.windowAnimations = atTop ? 0 : R.style.TitleBar;
- // 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);
+ manager.addView(mFakeTitleBar, params);
}
}
showFakeTitleBar();
}
}
+
private void hideFakeTitleBar() {
- if (mFakeTitleBar == null) return;
+ if (mFakeTitleBar.getParent() == null) return;
WindowManager.LayoutParams params = (WindowManager.LayoutParams)
- mFakeTitleBarHolder.getLayoutParams();
+ mFakeTitleBar.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
? 0 : R.style.TitleBar;
WindowManager manager
= (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- manager.updateViewLayout(mFakeTitleBarHolder, params);
- mFakeTitleBarHolder.removeView(mFakeTitleBar);
- manager.removeView(mFakeTitleBarHolder);
- mFakeTitleBar = null;
+ manager.updateViewLayout(mFakeTitleBar, params);
+ manager.removeView(mFakeTitleBar);
}
/**
openContextMenu(mTitleBar);
}
+ @Override
+ public void onContextMenuClosed(Menu menu) {
+ super.onContextMenuClosed(menu);
+ if (mInLoad) {
+ showFakeTitleBar();
+ }
+ }
+
/**
* onSaveInstanceState(Bundle map)
* onSaveInstanceState is called right before onStop(). The map contains
* the saved state.
*/
- @Override protected void onSaveInstanceState(Bundle outState) {
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this);
}
mTabControl.saveState(outState);
}
- @Override protected void onPause() {
+ @Override
+ protected void onPause() {
super.onPause();
if (mActivityInPause) {
.obtainMessage(RELEASE_WAKELOCK), WAKELOCK_TIMEOUT);
}
- // Clear the credentials toast if it is up
- if (mCredsDlg != null && mCredsDlg.isShowing()) {
- mCredsDlg.dismiss();
- }
- 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
// unregister network state listener
unregisterReceiver(mNetworkStateIntentReceiver);
WebView.disablePlatformNotifications();
-
- if (mSensorManager != null) {
- mSensorManager.unregisterListener(mSensorListener);
- }
}
- @Override protected void onDestroy() {
+ @Override
+ protected void onDestroy() {
if (LOGV_ENABLED) {
Log.v(LOGTAG, "BrowserActivity.onDestroy: this=" + this);
}
super.onDestroy();
+ if (mUploadMessage != null) {
+ mUploadMessage.onReceiveValue(null);
+ mUploadMessage = null;
+ }
+
if (mTabControl == null) return;
+ // Remove the fake title bar if it is there
+ hideFakeTitleBar();
+
// Remove the current tab and sub window
- TabControl.Tab t = mTabControl.getCurrentTab();
+ Tab t = mTabControl.getCurrentTab();
if (t != null) {
dismissSubWindow(t);
removeTabFromContentView(t);
// Destroy all the tabs
mTabControl.destroy();
WebIconDatabase.getInstance().close();
- if (mGlsConnection != null) {
- unbindService(mGlsConnection);
- mGlsConnection = null;
- }
-
- //
- // stop MASF proxy service
- //
- //Intent proxyServiceIntent = new Intent();
- //proxyServiceIntent.setComponent
- // (new ComponentName(
- // "com.android.masfproxyservice",
- // "com.android.masfproxyservice.MasfProxyService"));
- //stopService(proxyServiceIntent);
unregisterReceiver(mPackageInstallationReceiver);
+
+ // Stop watching the default geolocation permissions
+ mSystemAllowGeolocationOrigins.stop();
+ mSystemAllowGeolocationOrigins = null;
}
@Override
mPageInfoDialog.dismiss();
showPageInfo(
mPageInfoView,
- mPageInfoFromShowSSLCertificateOnError.booleanValue());
+ mPageInfoFromShowSSLCertificateOnError);
}
if (mSSLCertificateDialog != null) {
mSSLCertificateDialog.dismiss();
showHttpAuthentication(mHttpAuthHandler, null, null, title,
name, password, focusId);
}
- if (mFindDialog != null && mFindDialog.isShowing()) {
- mFindDialog.onConfigurationChanged(newConfig);
- }
}
- @Override public void onLowMemory() {
+ @Override
+ public void onLowMemory() {
super.onLowMemory();
mTabControl.freeMemory();
}
- private boolean resumeWebViewTimers() {
- if ((!mActivityInPause && !mPageStarted) ||
- (mActivityInPause && mPageStarted)) {
+ private void resumeWebViewTimers() {
+ Tab tab = mTabControl.getCurrentTab();
+ if (tab == null) return; // monkey can trigger this
+ boolean inLoad = tab.inLoad();
+ if ((!mActivityInPause && !inLoad) || (mActivityInPause && inLoad)) {
CookieSyncManager.getInstance().startSync();
- WebView w = mTabControl.getCurrentWebView();
+ WebView w = tab.getWebView();
if (w != null) {
w.resumeTimers();
}
- return true;
- } else {
- return false;
}
}
private boolean pauseWebViewTimers() {
- if (mActivityInPause && !mPageStarted) {
+ Tab tab = mTabControl.getCurrentTab();
+ boolean inLoad = tab.inLoad();
+ if (mActivityInPause && !inLoad) {
CookieSyncManager.getInstance().stopSync();
WebView w = mTabControl.getCurrentWebView();
if (w != null) {
}
}
- // FIXME: Do we want to call this when loading google for the first time?
- /*
- * This function is called when we are launching for the first time. We
- * are waiting for the login credentials before loading Google home
- * pages. This way the user will be logged in straight away.
- */
- private void waitForCredentials() {
- // Show a toast
- mCredsDlg = new ProgressDialog(this);
- mCredsDlg.setIndeterminate(true);
- mCredsDlg.setMessage(getText(R.string.retrieving_creds_dlg_msg));
- // If the user cancels the operation, then cancel the Google
- // Credentials request.
- mCredsDlg.setCancelMessage(mHandler.obtainMessage(CANCEL_CREDS_REQUEST));
- mCredsDlg.show();
-
- // We set a timeout for the retrieval of credentials in onResume()
- // as that is when we have freed up some CPU time to get
- // the login credentials.
- }
-
- /*
- * If we have received the credentials or we have timed out and we are
- * showing the credentials dialog, then it is time to move on.
- */
- private void resumeAfterCredentials() {
- if (mCredsDlg == null) {
- return;
- }
-
- // Clear the toast
- if (mCredsDlg.isShowing()) {
- mCredsDlg.dismiss();
- }
- mCredsDlg = null;
-
- // Clear any pending timeout
- mHandler.removeMessages(CANCEL_CREDS_REQUEST);
-
- // Load the page
- WebView w = mTabControl.getCurrentWebView();
- if (w != null) {
- w.loadUrl(mSettings.getHomePage());
- }
-
- // Update the settings, need to do this last as it can take a moment
- // to persist the settings. In the mean time we could be loading
- // content.
- mSettings.setLoginInitialized(this);
- }
-
// Open the icon database and retain all the icons for visited sites.
private void retainIconsOnStartup() {
final WebIconDatabase db = WebIconDatabase.getInstance();
db.open(getDir("icons", 0).getPath());
+ Cursor c = null;
try {
- Cursor c = Browser.getAllBookmarks(mResolver);
- if (!c.moveToFirst()) {
- c.deactivate();
- return;
+ c = Browser.getAllBookmarks(mResolver);
+ if (c.moveToFirst()) {
+ int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
+ do {
+ String url = c.getString(urlIndex);
+ db.retainIconForPageUrl(url);
+ } while (c.moveToNext());
}
- int urlIndex = c.getColumnIndex(Browser.BookmarkColumns.URL);
- do {
- String url = c.getString(urlIndex);
- db.retainIconForPageUrl(url);
- } while (c.moveToNext());
- c.deactivate();
} catch (IllegalStateException e) {
Log.e(LOGTAG, "retainIconsOnStartup", e);
+ } finally {
+ if (c!= null) c.close();
}
}
return mTabControl.getCurrentTopWebView();
}
+ TabControl getTabControl() {
+ return mTabControl;
+ }
+
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
// options selector, so set mCanChord to true so we can access them.
mCanChord = true;
int id = item.getItemId();
+ boolean result = true;
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;
+ Tab currentTab = mTabControl.getCurrentTab();
+ if (null == currentTab) {
+ result = false;
+ break;
}
- if (id == R.id.title_bar_share_page_url) {
- Browser.sendString(this, mainView.getUrl());
- } else {
- copy(mainView.getUrl());
+ WebView mainView = currentTab.getWebView();
+ if (null == mainView) {
+ result = false;
+ break;
}
+ copy(mainView.getUrl());
break;
// -- Browser context menu
case R.id.open_context_menu_id:
case R.id.copy_link_context_menu_id:
final WebView webView = getTopWindow();
if (null == webView) {
- return false;
+ result = false;
+ break;
}
final HashMap hrefMap = new HashMap();
hrefMap.put("webview", webView);
default:
// For other context menus
- return onOptionsItemSelected(item);
+ result = onOptionsItemSelected(item);
}
mCanChord = false;
- return true;
+ return result;
}
private Bundle createGoogleSearchSourceBundle(String source) {
Bundle bundle = new Bundle();
- bundle.putString(SearchManager.SOURCE, source);
+ bundle.putString(Search.SOURCE, source);
return bundle;
}
- /**
- * Overriding this to insert a local information bundle
- */
- @Override
- public boolean onSearchRequested() {
+ /* package */ void editUrl() {
if (mOptionsMenuOpen) closeOptionsMenu();
String url = (getTopWindow() == null) ? null : getTopWindow().getUrl();
startSearch(mSettings.getHomePage().equals(url) ? null : url, true,
- createGoogleSearchSourceBundle(GOOGLE_SEARCH_SOURCE_SEARCHKEY), false);
- return true;
+ null, false);
}
+ /**
+ * Overriding this to insert a local information bundle
+ */
@Override
public void startSearch(String initialQuery, boolean selectInitialQuery,
Bundle appSearchData, boolean globalSearch) {
* the current one, return false.
*/
/* package */ boolean switchToTab(int index) {
- TabControl.Tab tab = mTabControl.getTab(index);
- TabControl.Tab currentTab = mTabControl.getCurrentTab();
+ Tab tab = mTabControl.getTab(index);
+ Tab currentTab = mTabControl.getCurrentTab();
if (tab == null || tab == currentTab) {
return false;
}
return true;
}
- /* package */ TabControl.Tab openTabToHomePage() {
+ /* package */ Tab openTabToHomePage() {
return openTabAndShow(mSettings.getHomePage(), false, null);
}
/* package */ void closeCurrentWindow() {
- final TabControl.Tab current = mTabControl.getCurrentTab();
+ final Tab current = mTabControl.getCurrentTab();
if (mTabControl.getTabCount() == 1) {
// This is the last tab. Open a new one, with the home
// page and close the current one.
- TabControl.Tab newTab = openTabToHomePage();
+ openTabToHomePage();
closeTab(current);
return;
}
- final TabControl.Tab parent = current.getParentTab();
+ final Tab parent = current.getParentTab();
int indexToShow = -1;
if (parent != null) {
indexToShow = mTabControl.getTabIndex(parent);
break;
case R.id.goto_menu_id:
- onSearchRequested();
+ editUrl();
break;
case R.id.bookmarks_menu_id:
break;
case R.id.homepage_menu_id:
- TabControl.Tab current = mTabControl.getCurrentTab();
+ Tab current = mTabControl.getCurrentTab();
if (current != null) {
dismissSubWindow(current);
- current.getWebView().loadUrl(mSettings.getHomePage());
+ loadUrl(current.getWebView(), mSettings.getHomePage());
}
break;
case R.id.preferences_menu_id:
Intent intent = new Intent(this,
BrowserPreferencesPage.class);
+ intent.putExtra(BrowserPreferencesPage.CURRENT_PAGE,
+ getTopWindow().getUrl());
startActivityForResult(intent, PREFERENCES_PAGE);
break;
}
mFindDialog.setWebView(getTopWindow());
mFindDialog.show();
+ getTopWindow().setFindIsUp(true);
mMenuState = EMPTY_MENU;
break;
bookmarksOrHistoryPicker(true);
break;
+ case R.id.title_bar_share_page_url:
case R.id.share_page_menu_id:
- Browser.sendString(this, getTopWindow().getUrl(),
- getText(R.string.choosertitle_sharevia).toString());
+ Tab currentTab = mTabControl.getCurrentTab();
+ if (null == currentTab) {
+ mCanChord = false;
+ return false;
+ }
+ currentTab.populatePickerData();
+ sharePage(this, currentTab.getTitle(),
+ currentTab.getUrl(), currentTab.getFavicon(),
+ createScreenshot(currentTab.getWebView()));
break;
case R.id.dump_nav_menu_id:
getTopWindow().debugDump();
break;
+ case R.id.dump_counters_menu_id:
+ getTopWindow().dumpV8Counters();
+ break;
+
case R.id.zoom_in_menu_id:
getTopWindow().zoomIn();
break;
int menuid = item.getItemId();
for (int id = 0; id < WINDOW_SHORTCUT_ID_ARRAY.length; id++) {
if (WINDOW_SHORTCUT_ID_ARRAY[id] == menuid) {
- TabControl.Tab desiredTab = mTabControl.getTab(id);
+ Tab desiredTab = mTabControl.getTab(id);
if (desiredTab != null &&
desiredTab != mTabControl.getCurrentTab()) {
switchToTab(id);
mMenuState = R.id.MAIN_MENU;
}
- @Override public boolean onPrepareOptionsMenu(Menu menu)
- {
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
// This happens when the user begins to hold down the menu key, so
// allow them to chord to get a shortcut.
mCanChord = true;
.setEnabled(canGoForward);
menu.findItem(R.id.new_tab_menu_id).setEnabled(
- mTabControl.getTabCount() < TabControl.MAX_TABS);
+ mTabControl.canCreateNewTab());
// decide whether to show the share link option
PackageManager pm = getPackageManager();
final MenuItem nav = menu.findItem(R.id.dump_nav_menu_id);
nav.setVisible(isNavDump);
nav.setEnabled(isNavDump);
+
+ boolean showDebugSettings = mSettings.showDebugSettings();
+ final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id);
+ counter.setVisible(showDebugSettings);
+ counter.setEnabled(showDebugSettings);
+
break;
}
mCurrentMenuState = mMenuState;
@Override
public void onCreateContextMenu(ContextMenu menu, View v,
ContextMenuInfo menuInfo) {
+ if (v instanceof TitleBar) {
+ return;
+ }
WebView webview = (WebView) v;
WebView.HitTestResult result = webview.getHitTestResult();
if (result == null) {
menu.setHeaderView(titleView);
// decide whether to show the open link in new tab option
menu.findItem(R.id.open_newtab_context_menu_id).setVisible(
- mTabControl.getTabCount() < TabControl.MAX_TABS);
+ mTabControl.canCreateNewTab());
+ menu.findItem(R.id.bookmark_context_menu_id).setVisible(
+ Bookmarks.urlHasAcceptableScheme(extra));
PackageManager pm = getPackageManager();
Intent send = new Intent(Intent.ACTION_SEND);
send.setType("text/plain");
new Intent(Intent.ACTION_VIEW, Uri.parse(extra)));
menu.findItem(R.id.download_context_menu_id).
setOnMenuItemClickListener(new Download(extra));
+ menu.findItem(R.id.set_wallpaper_context_menu_id).
+ setOnMenuItemClickListener(new SetAsWallpaper(extra));
break;
default:
Log.w(LOGTAG, "We should not get here.");
break;
}
+ hideFakeTitleBar();
}
// Attach the given tab to the content view.
// this should only be called for the current tab.
- private void attachTabToContentView(TabControl.Tab t) {
+ private void attachTabToContentView(Tab t) {
// Attach the container that contains the main WebView and any other UI
// associated with the tab.
t.attachTabToContentView(mContentView);
if (mShouldShowErrorConsole) {
- ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true);
+ ErrorConsoleView errorConsole = t.getErrorConsole(true);
if (errorConsole.numberOfErrors() == 0) {
errorConsole.showConsole(ErrorConsoleView.SHOW_NONE);
} else {
}
mErrorConsoleContainer.addView(errorConsole,
- new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
+ new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
}
- 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);
+ if (t.isInVoiceSearchMode()) {
+ showVoiceTitleBar(t.getVoiceDisplayTitle());
+ } else {
+ revertVoiceTitleBar();
+ }
// 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) {
+ void attachSubWindow(Tab t) {
t.attachSubWindow(mContentView);
getTopWindow().requestFocus();
}
// Remove the given tab from the content view.
- private void removeTabFromContentView(TabControl.Tab t) {
+ private void removeTabFromContentView(Tab t) {
// Remove the container that contains the main WebView.
t.removeTabFromContentView(mContentView);
- if (mTabControl.getCurrentErrorConsole(false) != null) {
- mErrorConsoleContainer.removeView(mTabControl.getCurrentErrorConsole(false));
+ ErrorConsoleView errorConsole = t.getErrorConsole(false);
+ if (errorConsole != null) {
+ mErrorConsoleContainer.removeView(errorConsole);
}
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) {
+ /* package */ void dismissSubWindow(Tab t) {
t.removeSubWindow(mContentView);
- // Tell the TabControl to dismiss the subwindow. This will destroy
- // the WebView.
- mTabControl.dismissSubWindow(t);
+ // dismiss the subwindow. This will destroy the WebView.
+ t.dismissSubWindow();
getTopWindow().requestFocus();
}
// A wrapper function of {@link #openTabAndShow(UrlData, boolean, String)}
// that accepts url as string.
- private TabControl.Tab openTabAndShow(String url, boolean closeOnExit,
- String appId) {
+ private Tab openTabAndShow(String url, boolean closeOnExit, String appId) {
return openTabAndShow(new UrlData(url), closeOnExit, appId);
}
// This method does a ton of stuff. It will attempt to create a new tab
// if we haven't reached MAX_TABS. Otherwise it uses the current tab. If
// url isn't null, it will load the given url.
- /* package */ TabControl.Tab openTabAndShow(UrlData urlData,
- boolean closeOnExit, String appId) {
- final boolean newTab = mTabControl.getTabCount() != TabControl.MAX_TABS;
- final TabControl.Tab currentTab = mTabControl.getCurrentTab();
- if (newTab) {
- final TabControl.Tab tab = mTabControl.createNewTab(
- closeOnExit, appId, urlData.mUrl);
+ /* package */Tab openTabAndShow(UrlData urlData, boolean closeOnExit,
+ String appId) {
+ final Tab currentTab = mTabControl.getCurrentTab();
+ if (mTabControl.canCreateNewTab()) {
+ final Tab tab = mTabControl.createNewTab(closeOnExit, appId,
+ urlData.mUrl);
WebView webview = tab.getWebView();
// If the last tab was removed from the active tabs page, currentTab
// will be null.
mTabControl.setCurrentTab(tab);
attachTabToContentView(tab);
if (!urlData.isEmpty()) {
- urlData.loadIn(webview);
+ loadUrlDataIn(tab, urlData);
}
return tab;
} else {
dismissSubWindow(currentTab);
if (!urlData.isEmpty()) {
// Load the given url.
- urlData.loadIn(currentTab.getWebView());
+ loadUrlDataIn(currentTab, urlData);
}
+ return currentTab;
}
- return currentTab;
}
- private TabControl.Tab openTab(String url) {
+ private Tab openTab(String url) {
if (mSettings.openInBackground()) {
- TabControl.Tab t = mTabControl.createNewTab();
+ Tab t = mTabControl.createNewTab();
if (t != null) {
WebView view = t.getWebView();
- view.loadUrl(url);
+ loadUrl(view, url);
}
return t;
} else {
}
}
+ private class SetAsWallpaper extends Thread implements
+ OnMenuItemClickListener, DialogInterface.OnCancelListener {
+ private URL mUrl;
+ private ProgressDialog mWallpaperProgress;
+ private boolean mCanceled = false;
+
+ public SetAsWallpaper(String url) {
+ try {
+ mUrl = new URL(url);
+ } catch (MalformedURLException e) {
+ mUrl = null;
+ }
+ }
+
+ public void onCancel(DialogInterface dialog) {
+ mCanceled = true;
+ }
+
+ public boolean onMenuItemClick(MenuItem item) {
+ if (mUrl != null) {
+ // The user may have tried to set a image with a large file size as their
+ // background so it may take a few moments to perform the operation. Display
+ // a progress spinner while it is working.
+ mWallpaperProgress = new ProgressDialog(BrowserActivity.this);
+ mWallpaperProgress.setIndeterminate(true);
+ mWallpaperProgress.setMessage(getText(R.string.progress_dialog_setting_wallpaper));
+ mWallpaperProgress.setCancelable(true);
+ mWallpaperProgress.setOnCancelListener(this);
+ mWallpaperProgress.show();
+ start();
+ }
+ return true;
+ }
+
+ public void run() {
+ Drawable oldWallpaper = BrowserActivity.this.getWallpaper();
+ try {
+ // TODO: This will cause the resource to be downloaded again, when we
+ // should in most cases be able to grab it from the cache. To fix this
+ // we should query WebCore to see if we can access a cached version and
+ // instead open an input stream on that. This pattern could also be used
+ // in the download manager where the same problem exists.
+ InputStream inputstream = mUrl.openStream();
+ if (inputstream != null) {
+ setWallpaper(inputstream);
+ }
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Unable to set new wallpaper");
+ // Act as though the user canceled the operation so we try to
+ // restore the old wallpaper.
+ mCanceled = true;
+ }
+
+ if (mCanceled) {
+ // Restore the old wallpaper if the user cancelled whilst we were setting
+ // the new wallpaper.
+ int width = oldWallpaper.getIntrinsicWidth();
+ int height = oldWallpaper.getIntrinsicHeight();
+ Bitmap bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
+ Canvas canvas = new Canvas(bm);
+ oldWallpaper.setBounds(0, 0, width, height);
+ oldWallpaper.draw(canvas);
+ try {
+ setWallpaper(bm);
+ } catch (IOException e) {
+ Log.e(LOGTAG, "Unable to restore old wallpaper.");
+ }
+ mCanceled = false;
+ }
+
+ if (mWallpaperProgress.isShowing()) {
+ mWallpaperProgress.dismiss();
+ }
+ }
+ }
+
private void copy(CharSequence text) {
try {
IClipboard clip = IClipboard.Stub.asInterface(ServiceManager.getService("clipboard"));
* call resetTitleAndRevertLockIcon.
*/
/* package */ void resetTitleAndRevertLockIcon() {
- revertLockIcon();
+ mTabControl.getCurrentTab().revertLockIcon();
+ updateLockIconToLatest();
resetTitleIconAndProgress();
}
}
resetTitleAndIcon(current);
int progress = current.getProgress();
- mWebChromeClient.onProgressChanged(current, progress);
+ current.getWebChromeClient().onProgressChanged(current, progress);
}
// Reset the title and the icon based on the given item.
* @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) {
+ void setUrlTitle(String url, String title) {
mUrl = url;
mTitle = title;
- mTitleBar.setTitleAndUrl(title, url);
- if (mFakeTitleBar != null) {
- mFakeTitleBar.setTitleAndUrl(title, url);
- }
+ // If we are in voice search mode, the title has already been set.
+ if (mTabControl.getCurrentTab().isInVoiceSearchMode()) return;
+ mTitleBar.setDisplayTitle(url);
+ mFakeTitleBar.setDisplayTitle(url);
}
/**
}
// Set the favicon in the title bar.
- private void setFavicon(Bitmap icon) {
+ void setFavicon(Bitmap icon) {
mTitleBar.setFavicon(icon);
- if (mFakeTitleBar != null) {
- mFakeTitleBar.setFavicon(icon);
- }
- }
-
- /**
- * Saves the current lock-icon state before resetting
- * the lock icon. If we have an error, we may need to
- * roll back to the previous state.
- */
- private void saveLockIcon() {
- mPrevLockType = mLockIconType;
- }
-
- /**
- * Reverts the lock-icon state to the last saved state,
- * for example, if we had an error, and need to cancel
- * the load.
- */
- private void revertLockIcon() {
- mLockIconType = mPrevLockType;
-
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "BrowserActivity.revertLockIcon:" +
- " revert lock icon to " + mLockIconType);
- }
-
- updateLockIconToLatest();
+ mFakeTitleBar.setFavicon(icon);
}
/**
* Close the tab, remove its associated title bar, and adjust mTabControl's
* current tab to a valid value.
*/
- /* package */ void closeTab(TabControl.Tab t) {
+ /* package */ void closeTab(Tab t) {
int currentIndex = mTabControl.getCurrentIndex();
int removeIndex = mTabControl.getTabIndex(t);
mTabControl.removeTab(t);
resetTitleIconAndProgress();
}
- private void goBackOnePageOrQuit() {
- TabControl.Tab current = mTabControl.getCurrentTab();
+ /* package */ void goBackOnePageOrQuit() {
+ Tab current = mTabControl.getCurrentTab();
if (current == null) {
/*
* Instead of finishing the activity, simply push this to the back
} else {
// Check to see if we are closing a window that was created by
// another window. If so, we switch back to that window.
- TabControl.Tab parent = current.getParentTab();
+ Tab parent = current.getParentTab();
if (parent != null) {
switchToTab(mTabControl.getTabIndex(parent));
// Now we close the other tab
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;
+ // force the tab's inLoad() to be false as we are going to
+ // either finish the activity or remove the tab. This will
+ // ensure pauseWebViewTimers() taking action.
+ mTabControl.getCurrentTab().clearInLoad();
if (mTabControl.getTabCount() == 1) {
finish();
return;
}
}
+ boolean isMenuDown() {
+ return mMenuIsDown;
+ }
+
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // Even if MENU is already held down, we need to call to super to open
+ // the IME on long press.
+ if (KeyEvent.KEYCODE_MENU == keyCode) {
+ mMenuIsDown = true;
+ return super.onKeyDown(keyCode, 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
if (event.isTracking() && !event.isCanceled()) {
if (mCustomView != null) {
// if a custom view is showing, hide it
- mWebChromeClient.onHideCustomView();
+ mTabControl.getCurrentWebView().getWebChromeClient()
+ .onHideCustomView();
} else if (mActiveTabsPage != null) {
// if tab page is showing, hide it
removeActiveTabPage(true);
resetTitleAndRevertLockIcon();
WebView w = getTopWindow();
w.stopLoading();
- mWebViewClient.onPageFinished(w, w.getUrl());
+ // FIXME: before refactor, it is using mWebViewClient. So I keep the
+ // same logic here. But for subwindow case, should we call into the main
+ // WebView's onPageFinished as we never call its onPageStarted and if
+ // the page finishes itself, we don't call onPageFinished.
+ mTabControl.getCurrentWebView().getWebViewClient().onPageFinished(w,
+ w.getUrl());
cancelStopToast();
mStopToast = Toast
mStopToast.show();
}
+ boolean didUserStopLoading() {
+ return mDidStopLoad;
+ }
+
private void cancelStopToast() {
if (mStopToast != null) {
mStopToast.cancel();
}
}
- // called by a non-UI thread to post the message
- public void postMessage(int what, int arg1, int arg2, Object obj) {
- mHandler.sendMessage(mHandler.obtainMessage(what, arg1, arg2, obj));
+ // called by a UI or non-UI thread to post the message
+ public void postMessage(int what, int arg1, int arg2, Object obj,
+ long delayMillis) {
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(what, arg1, arg2,
+ obj), delayMillis);
+ }
+
+ // called by a UI or non-UI thread to remove the message
+ void removeMessages(int what, Object object) {
+ mHandler.removeMessages(what, object);
}
// public message ids
// Message Ids
private static final int FOCUS_NODE_HREF = 102;
- private static final int CANCEL_CREDS_REQUEST = 103;
private static final int RELEASE_WAKELOCK = 107;
- private static final int UPDATE_BOOKMARK_THUMBNAIL = 108;
+ static final int UPDATE_BOOKMARK_THUMBNAIL = 108;
// Private handler for handling javascript and saving passwords
private Handler mHandler = new Handler() {
case FOCUS_NODE_HREF:
{
String url = (String) msg.getData().get("url");
+ String title = (String) msg.getData().get("title");
if (url == null || url.length() == 0) {
break;
}
switch (msg.arg1) {
case R.id.open_context_menu_id:
case R.id.view_image_context_menu_id:
- loadURL(getTopWindow(), url);
+ loadUrlFromContext(getTopWindow(), url);
break;
case R.id.open_newtab_context_menu_id:
- final TabControl.Tab parent = mTabControl
- .getCurrentTab();
- final TabControl.Tab newTab = openTab(url);
+ final Tab parent = mTabControl.getCurrentTab();
+ final Tab newTab = openTab(url);
if (newTab != parent) {
parent.addChildTab(newTab);
}
Intent intent = new Intent(BrowserActivity.this,
AddBookmarkPage.class);
intent.putExtra("url", url);
+ intent.putExtra("title", title);
startActivity(intent);
break;
case R.id.share_link_context_menu_id:
- Browser.sendString(BrowserActivity.this, url,
- getText(R.string.choosertitle_sharevia).toString());
+ // See if this site has been visited before
+ StringBuilder sb = new StringBuilder(
+ Browser.BookmarkColumns.URL + " = ");
+ DatabaseUtils.appendEscapedSQLString(sb, url);
+ Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
+ Browser.HISTORY_PROJECTION,
+ sb.toString(),
+ null,
+ null);
+ if (c.moveToFirst()) {
+ // The site has been visited before, so grab the
+ // info from the database.
+ Bitmap favicon = null;
+ Bitmap thumbnail = null;
+ String linkTitle = c.getString(Browser.
+ HISTORY_PROJECTION_TITLE_INDEX);
+ byte[] data = c.getBlob(Browser.
+ HISTORY_PROJECTION_FAVICON_INDEX);
+ if (data != null) {
+ favicon = BitmapFactory.decodeByteArray(
+ data, 0, data.length);
+ }
+ data = c.getBlob(Browser.
+ HISTORY_PROJECTION_THUMBNAIL_INDEX);
+ if (data != null) {
+ thumbnail = BitmapFactory.decodeByteArray(
+ data, 0, data.length);
+ }
+ sharePage(BrowserActivity.this,
+ linkTitle, url, favicon, thumbnail);
+ } else {
+ Browser.sendString(BrowserActivity.this, url,
+ getString(
+ R.string.choosertitle_sharevia));
+ }
break;
case R.id.copy_link_context_menu_id:
copy(url);
}
case LOAD_URL:
- loadURL(getTopWindow(), (String) msg.obj);
+ loadUrlFromContext(getTopWindow(), (String) msg.obj);
break;
case STOP_LOAD:
stopLoading();
break;
- case CANCEL_CREDS_REQUEST:
- resumeAfterCredentials();
- break;
-
case RELEASE_WAKELOCK:
if (mWakeLock.isHeld()) {
mWakeLock.release();
+ // if we reach here, Browser should be still in the
+ // background loading after WAKELOCK_TIMEOUT (5-min).
+ // To avoid burning the battery, stop loading.
+ mTabControl.stopAllLoading();
}
break;
}
};
+ /**
+ * Share a page, providing the title, url, favicon, and a screenshot. Uses
+ * an {@link Intent} to launch the Activity chooser.
+ * @param c Context used to launch a new Activity.
+ * @param title Title of the page. Stored in the Intent with
+ * {@link Intent#EXTRA_SUBJECT}
+ * @param url URL of the page. Stored in the Intent with
+ * {@link Intent#EXTRA_TEXT}
+ * @param favicon Bitmap of the favicon for the page. Stored in the Intent
+ * with {@link Browser#EXTRA_SHARE_FAVICON}
+ * @param screenshot Bitmap of a screenshot of the page. Stored in the
+ * Intent with {@link Browser#EXTRA_SHARE_SCREENSHOT}
+ */
+ public static final void sharePage(Context c, String title, String url,
+ Bitmap favicon, Bitmap screenshot) {
+ Intent send = new Intent(Intent.ACTION_SEND);
+ send.setType("text/plain");
+ send.putExtra(Intent.EXTRA_TEXT, url);
+ send.putExtra(Intent.EXTRA_SUBJECT, title);
+ send.putExtra(Browser.EXTRA_SHARE_FAVICON, favicon);
+ send.putExtra(Browser.EXTRA_SHARE_SCREENSHOT, screenshot);
+ try {
+ c.startActivity(Intent.createChooser(send, c.getString(
+ R.string.choosertitle_sharevia)));
+ } catch(android.content.ActivityNotFoundException ex) {
+ // if no app handles it, do nothing
+ }
+ }
+
private void updateScreenshot(WebView view) {
// If this is a bookmarked site, add a screenshot to the database.
// FIXME: When should we update? Every time?
return null;
}
Bitmap bm = Bitmap.createBitmap(getDesiredThumbnailWidth(this),
- getDesiredThumbnailHeight(this), Bitmap.Config.ARGB_4444);
+ getDesiredThumbnailHeight(this), Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bm);
// May need to tweak these values to determine what is the
// best scale factor
int thumbnailWidth = thumbnail.getWidth();
+ int thumbnailHeight = thumbnail.getHeight();
+ float scaleFactorX = 1.0f;
+ float scaleFactorY = 1.0f;
if (thumbnailWidth > 0) {
- float scaleFactor = (float) getDesiredThumbnailWidth(this) /
+ scaleFactorX = (float) getDesiredThumbnailWidth(this) /
(float)thumbnailWidth;
- canvas.scale(scaleFactor, scaleFactor);
+ } else {
+ return null;
+ }
+
+ if (view.getWidth() > view.getHeight() &&
+ thumbnailHeight < view.getHeight() && thumbnailHeight > 0) {
+ // If the device is in landscape and the page is shorter
+ // than the height of the view, stretch the thumbnail to fill the
+ // space.
+ scaleFactorY = (float) getDesiredThumbnailHeight(this) /
+ (float)thumbnailHeight;
+ } else {
+ // In the portrait case, this looks nice.
+ scaleFactorY = scaleFactorX;
}
+
+ canvas.scale(scaleFactorX, scaleFactorY);
+
thumbnail.draw(canvas);
return bm;
}
// -------------------------------------------------------------------------
- // WebViewClient implementation.
+ // Helper function for WebViewClient.
//-------------------------------------------------------------------------
// Use in overrideUrlLoading
/* package */ final static String SCHEME_WTAI_SD = "wtai://wp/sd;";
/* package */ final static String SCHEME_WTAI_AP = "wtai://wp/ap;";
- /* package */ WebViewClient getWebViewClient() {
- return mWebViewClient;
- }
-
- private void updateIcon(WebView view, Bitmap icon) {
- if (icon != null) {
- BrowserBookmarksAdapter.updateBookmarkFavicon(mResolver,
- view.getOriginalUrl(), view.getUrl(), icon);
+ // Keep this initial progress in sync with initialProgressValue (* 100)
+ // in ProgressTracker.cpp
+ private final static int INITIAL_PROGRESS = 10;
+
+ void onPageStarted(WebView view, String url, Bitmap favicon) {
+ // when BrowserActivity just starts, onPageStarted may be called before
+ // onResume as it is triggered from onCreate. Call resumeWebViewTimers
+ // to start the timer. As we won't switch tabs while an activity is in
+ // pause state, we can ensure calling resume and pause in pair.
+ if (mActivityInPause) resumeWebViewTimers();
+
+ resetLockIcon(url);
+ setUrlTitle(url, null);
+ setFavicon(favicon);
+ // Show some progress so that the user knows the page is beginning to
+ // load
+ onProgressChanged(view, INITIAL_PROGRESS);
+ mDidStopLoad = false;
+ if (!mIsNetworkUp) createAndShowNetworkDialog();
+
+ if (mSettings.isTracing()) {
+ String host;
+ try {
+ WebAddress uri = new WebAddress(url);
+ host = uri.mHost;
+ } catch (android.net.ParseException ex) {
+ host = "browser";
+ }
+ host = host.replace('.', '_');
+ host += ".trace";
+ mInTrace = true;
+ Debug.startMethodTracing(host, 20 * 1024 * 1024);
}
- setFavicon(icon);
- }
- private void updateIcon(String url, Bitmap icon) {
- if (icon != null) {
- BrowserBookmarksAdapter.updateBookmarkFavicon(mResolver,
- null, url, icon);
+ // Performance probe
+ if (false) {
+ mStart = SystemClock.uptimeMillis();
+ mProcessStart = Process.getElapsedCpuTime();
+ long[] sysCpu = new long[7];
+ if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
+ sysCpu, null)) {
+ mUserStart = sysCpu[0] + sysCpu[1];
+ mSystemStart = sysCpu[2];
+ mIdleStart = sysCpu[3];
+ mIrqStart = sysCpu[4] + sysCpu[5] + sysCpu[6];
+ }
+ mUiStart = SystemClock.currentThreadTimeMillis();
}
- setFavicon(icon);
}
- private final WebViewClient mWebViewClient = new WebViewClient() {
- @Override
- public void onPageStarted(WebView view, String url, Bitmap favicon) {
- 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();
- if (mShouldShowErrorConsole) {
- errorConsole.showConsole(ErrorConsoleView.SHOW_NONE);
- }
- }
-
- // Call updateIcon instead of setFavicon so the bookmark
- // database can be updated.
- updateIcon(url, favicon);
-
- if (mSettings.isTracing()) {
- String host;
- try {
- WebAddress uri = new WebAddress(url);
- host = uri.mHost;
- } catch (android.net.ParseException ex) {
- host = "browser";
- }
- host = host.replace('.', '_');
- host += ".trace";
- mInTrace = true;
- Debug.startMethodTracing(host, 20 * 1024 * 1024);
- }
-
- // Performance probe
- if (false) {
- mStart = SystemClock.uptimeMillis();
- mProcessStart = Process.getElapsedCpuTime();
- long[] sysCpu = new long[7];
- if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
- sysCpu, null)) {
- mUserStart = sysCpu[0] + sysCpu[1];
- mSystemStart = sysCpu[2];
- mIdleStart = sysCpu[3];
- mIrqStart = sysCpu[4] + sysCpu[5] + sysCpu[6];
- }
- mUiStart = SystemClock.currentThreadTimeMillis();
- }
-
- if (!mPageStarted) {
- mPageStarted = true;
- // if onResume() has been called, resumeWebViewTimers() does
- // nothing.
- resumeWebViewTimers();
- }
-
- // reset sync timer to avoid sync starts during loading a page
- CookieSyncManager.getInstance().resetSync();
-
- mInLoad = true;
- mDidStopLoad = false;
- showFakeTitleBar();
- updateInLoadMenuItems();
- if (!mIsNetworkUp) {
- createAndShowNetworkDialog();
- if (view != null) {
- view.setNetworkAvailable(false);
+ void onPageFinished(WebView view, String url) {
+ // Reset the title and icon in case we stopped a provisional load.
+ resetTitleAndIcon(view);
+ // Update the lock icon image only once we are done loading
+ updateLockIconToLatest();
+ // pause the WebView timer and release the wake lock if it is finished
+ // while BrowserActivity is in pause state.
+ if (mActivityInPause && pauseWebViewTimers()) {
+ if (mWakeLock.isHeld()) {
+ mHandler.removeMessages(RELEASE_WAKELOCK);
+ mWakeLock.release();
+ }
+ }
+
+ // Performance probe
+ if (false) {
+ long[] sysCpu = new long[7];
+ if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
+ sysCpu, null)) {
+ String uiInfo = "UI thread used "
+ + (SystemClock.currentThreadTimeMillis() - mUiStart)
+ + " ms";
+ if (LOGD_ENABLED) {
+ Log.d(LOGTAG, uiInfo);
+ }
+ //The string that gets written to the log
+ String performanceString = "It took total "
+ + (SystemClock.uptimeMillis() - mStart)
+ + " ms clock time to load the page."
+ + "\nbrowser process used "
+ + (Process.getElapsedCpuTime() - mProcessStart)
+ + " ms, user processes used "
+ + (sysCpu[0] + sysCpu[1] - mUserStart) * 10
+ + " ms, kernel used "
+ + (sysCpu[2] - mSystemStart) * 10
+ + " ms, idle took " + (sysCpu[3] - mIdleStart) * 10
+ + " ms and irq took "
+ + (sysCpu[4] + sysCpu[5] + sysCpu[6] - mIrqStart)
+ * 10 + " ms, " + uiInfo;
+ if (LOGD_ENABLED) {
+ Log.d(LOGTAG, performanceString + "\nWebpage: " + url);
}
- }
- }
-
- @Override
- public void onPageFinished(WebView view, String url) {
- // Reset the title and icon in case we stopped a provisional
- // 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();
-
- // Performance probe
- if (false) {
- long[] sysCpu = new long[7];
- if (Process.readProcFile("/proc/stat", SYSTEM_CPU_FORMAT, null,
- sysCpu, null)) {
- String uiInfo = "UI thread used "
- + (SystemClock.currentThreadTimeMillis() - mUiStart)
- + " ms";
- if (LOGD_ENABLED) {
- Log.d(LOGTAG, uiInfo);
+ if (url != null) {
+ // strip the url to maintain consistency
+ String newUrl = new String(url);
+ if (newUrl.startsWith("http://www.")) {
+ newUrl = newUrl.substring(11);
+ } else if (newUrl.startsWith("http://")) {
+ newUrl = newUrl.substring(7);
+ } else if (newUrl.startsWith("https://www.")) {
+ newUrl = newUrl.substring(12);
+ } else if (newUrl.startsWith("https://")) {
+ newUrl = newUrl.substring(8);
}
- //The string that gets written to the log
- String performanceString = "It took total "
- + (SystemClock.uptimeMillis() - mStart)
- + " ms clock time to load the page."
- + "\nbrowser process used "
- + (Process.getElapsedCpuTime() - mProcessStart)
- + " ms, user processes used "
- + (sysCpu[0] + sysCpu[1] - mUserStart) * 10
- + " ms, kernel used "
- + (sysCpu[2] - mSystemStart) * 10
- + " ms, idle took " + (sysCpu[3] - mIdleStart) * 10
- + " ms and irq took "
- + (sysCpu[4] + sysCpu[5] + sysCpu[6] - mIrqStart)
- * 10 + " ms, " + uiInfo;
if (LOGD_ENABLED) {
- Log.d(LOGTAG, performanceString + "\nWebpage: " + url);
- }
- if (url != null) {
- // strip the url to maintain consistency
- String newUrl = new String(url);
- if (newUrl.startsWith("http://www.")) {
- newUrl = newUrl.substring(11);
- } else if (newUrl.startsWith("http://")) {
- newUrl = newUrl.substring(7);
- } else if (newUrl.startsWith("https://www.")) {
- newUrl = newUrl.substring(12);
- } else if (newUrl.startsWith("https://")) {
- newUrl = newUrl.substring(8);
- }
- if (LOGD_ENABLED) {
- Log.d(LOGTAG, newUrl + " loaded");
- }
- /*
- if (sWhiteList.contains(newUrl)) {
- // The string that gets pushed to the statistcs
- // service
- performanceString = performanceString
- + "\nWebpage: "
- + newUrl
- + "\nCarrier: "
- + android.os.SystemProperties
- .get("gsm.sim.operator.alpha");
- if (mWebView != null
- && mWebView.getContext() != null
- && mWebView.getContext().getSystemService(
- Context.CONNECTIVITY_SERVICE) != null) {
- ConnectivityManager cManager =
- (ConnectivityManager) mWebView
- .getContext().getSystemService(
- Context.CONNECTIVITY_SERVICE);
- NetworkInfo nInfo = cManager
- .getActiveNetworkInfo();
- if (nInfo != null) {
- performanceString = performanceString
- + "\nNetwork Type: "
- + nInfo.getType().toString();
- }
- }
- Checkin.logEvent(mResolver,
- Checkin.Events.Tag.WEBPAGE_LOAD,
- performanceString);
- Log.w(LOGTAG, "pushed to the statistics service");
- }
- */
+ Log.d(LOGTAG, newUrl + " loaded");
}
}
- }
-
- if (mInTrace) {
- mInTrace = false;
- Debug.stopMethodTracing();
}
+ }
- if (mPageStarted) {
- mPageStarted = false;
- // pauseWebViewTimers() will do nothing and return false if
- // onPause() is not called yet.
- if (pauseWebViewTimers()) {
- if (mWakeLock.isHeld()) {
- mHandler.removeMessages(RELEASE_WAKELOCK);
- mWakeLock.release();
- }
- }
- }
+ if (mInTrace) {
+ mInTrace = false;
+ Debug.stopMethodTracing();
}
+ }
- // return true if want to hijack the url to let another app to handle it
- @Override
- public boolean shouldOverrideUrlLoading(WebView view, String url) {
- if (url.startsWith(SCHEME_WTAI)) {
- // wtai://wp/mc;number
- // number=string(phone-number)
- if (url.startsWith(SCHEME_WTAI_MC)) {
- Intent intent = new Intent(Intent.ACTION_VIEW,
- Uri.parse(WebView.SCHEME_TEL +
- url.substring(SCHEME_WTAI_MC.length())));
- startActivity(intent);
- return true;
- }
- // wtai://wp/sd;dtmf
- // dtmf=string(dialstring)
- if (url.startsWith(SCHEME_WTAI_SD)) {
- // TODO
- // only send when there is active voice connection
- return false;
- }
- // wtai://wp/ap;number;name
- // number=string(phone-number)
- // name=string
- if (url.startsWith(SCHEME_WTAI_AP)) {
- // TODO
- return false;
- }
+ boolean shouldOverrideUrlLoading(WebView view, String url) {
+ if (url.startsWith(SCHEME_WTAI)) {
+ // wtai://wp/mc;number
+ // number=string(phone-number)
+ if (url.startsWith(SCHEME_WTAI_MC)) {
+ Intent intent = new Intent(Intent.ACTION_VIEW,
+ Uri.parse(WebView.SCHEME_TEL +
+ url.substring(SCHEME_WTAI_MC.length())));
+ startActivity(intent);
+ return true;
}
-
- // The "about:" schemes are internal to the browser; don't
- // want these to be dispatched to other apps.
- if (url.startsWith("about:")) {
+ // wtai://wp/sd;dtmf
+ // dtmf=string(dialstring)
+ if (url.startsWith(SCHEME_WTAI_SD)) {
+ // TODO: only send when there is active voice connection
return false;
}
-
- Intent intent;
-
- // perform generic parsing of the URI to turn it into an Intent.
- try {
- intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
- } catch (URISyntaxException ex) {
- Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
+ // wtai://wp/ap;number;name
+ // number=string(phone-number)
+ // name=string
+ if (url.startsWith(SCHEME_WTAI_AP)) {
+ // TODO
return false;
}
-
- // check whether the intent can be resolved. If not, we will see
- // whether we can download it from the Market.
- if (getPackageManager().resolveActivity(intent, 0) == null) {
- String packagename = intent.getPackage();
- if (packagename != null) {
- intent = new Intent(Intent.ACTION_VIEW, Uri
- .parse("market://search?q=pname:" + packagename));
- intent.addCategory(Intent.CATEGORY_BROWSABLE);
- startActivity(intent);
- return true;
- } else {
- return false;
- }
- }
-
- // sanitize the Intent, ensuring web pages can not bypass browser
- // security (only access to BROWSABLE activities).
- intent.addCategory(Intent.CATEGORY_BROWSABLE);
- intent.setComponent(null);
- try {
- if (startActivityIfNeeded(intent, -1)) {
- return true;
- }
- } catch (ActivityNotFoundException ex) {
- // ignore the error. If no application can handle the URL,
- // eg about:blank, assume the browser can handle it.
- }
-
- if (mMenuIsDown) {
- openTab(url);
- closeOptionsMenu();
- return true;
- }
-
- return false;
- }
-
- /**
- * Updates the lock icon. This method is called when we discover another
- * resource to be loaded for this page (for example, javascript). While
- * we update the icon type, we do not update the lock icon itself until
- * we are done loading, it is slightly more secure this way.
- */
- @Override
- public void onLoadResource(WebView view, String url) {
- if (url != null && url.length() > 0) {
- // It is only if the page claims to be secure
- // that we may have to update the lock:
- if (mLockIconType == LOCK_ICON_SECURE) {
- // If NOT a 'safe' url, change the lock to mixed content!
- if (!(URLUtil.isHttpsUrl(url) || URLUtil.isDataUrl(url) || URLUtil.isAboutUrl(url))) {
- mLockIconType = LOCK_ICON_MIXED;
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "BrowserActivity.updateLockIcon:" +
- " updated lock icon to " + mLockIconType + " due to " + url);
- }
- }
- }
- }
- }
-
- /**
- * Show the dialog, asking the user if they would like to continue after
- * an excessive number of HTTP redirects.
- */
- @Override
- public void onTooManyRedirects(WebView view, final Message cancelMsg,
- final Message continueMsg) {
- new AlertDialog.Builder(BrowserActivity.this)
- .setTitle(R.string.browserFrameRedirect)
- .setMessage(R.string.browserFrame307Post)
- .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- continueMsg.sendToTarget();
- }})
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- cancelMsg.sendToTarget();
- }})
- .setOnCancelListener(new OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- cancelMsg.sendToTarget();
- }})
- .show();
- }
-
- // Container class for the next error dialog that needs to be
- // displayed.
- class ErrorDialog {
- public final int mTitle;
- public final String mDescription;
- public final int mError;
- ErrorDialog(int title, String desc, int error) {
- mTitle = title;
- mDescription = desc;
- mError = error;
- }
- };
-
- private void processNextError() {
- if (mQueuedErrors == null) {
- return;
- }
- // The first one is currently displayed so just remove it.
- mQueuedErrors.removeFirst();
- if (mQueuedErrors.size() == 0) {
- mQueuedErrors = null;
- return;
- }
- showError(mQueuedErrors.getFirst());
- }
-
- private DialogInterface.OnDismissListener mDialogListener =
- new DialogInterface.OnDismissListener() {
- public void onDismiss(DialogInterface d) {
- processNextError();
- }
- };
- private LinkedList<ErrorDialog> mQueuedErrors;
-
- private void queueError(int err, String desc) {
- if (mQueuedErrors == null) {
- mQueuedErrors = new LinkedList<ErrorDialog>();
- }
- for (ErrorDialog d : mQueuedErrors) {
- if (d.mError == err) {
- // Already saw a similar error, ignore the new one.
- return;
- }
- }
- ErrorDialog errDialog = new ErrorDialog(
- err == WebViewClient.ERROR_FILE_NOT_FOUND ?
- R.string.browserFrameFileErrorLabel :
- R.string.browserFrameNetworkErrorLabel,
- desc, err);
- mQueuedErrors.addLast(errDialog);
-
- // Show the dialog now if the queue was empty.
- if (mQueuedErrors.size() == 1) {
- showError(errDialog);
- }
- }
-
- private void showError(ErrorDialog errDialog) {
- AlertDialog d = new AlertDialog.Builder(BrowserActivity.this)
- .setTitle(errDialog.mTitle)
- .setMessage(errDialog.mDescription)
- .setPositiveButton(R.string.ok, null)
- .create();
- d.setOnDismissListener(mDialogListener);
- d.show();
- }
-
- /**
- * Show a dialog informing the user of the network error reported by
- * WebCore.
- */
- @Override
- public void onReceivedError(WebView view, int errorCode,
- String description, String failingUrl) {
- 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
- + " " + description);
-
- // We need to reset the title after an error.
- resetTitleAndRevertLockIcon();
- }
-
- /**
- * Check with the user if it is ok to resend POST data as the page they
- * are trying to navigate to is the result of a POST.
- */
- @Override
- public void onFormResubmission(WebView view, final Message dontResend,
- final Message resend) {
- new AlertDialog.Builder(BrowserActivity.this)
- .setTitle(R.string.browserFrameFormResubmitLabel)
- .setMessage(R.string.browserFrameFormResubmitMessage)
- .setPositiveButton(R.string.ok, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- resend.sendToTarget();
- }})
- .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int which) {
- dontResend.sendToTarget();
- }})
- .setOnCancelListener(new OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- dontResend.sendToTarget();
- }})
- .show();
}
- /**
- * Insert the url into the visited history database.
- * @param url The url to be inserted.
- * @param isReload True if this url is being reloaded.
- * FIXME: Not sure what to do when reloading the page.
- */
- @Override
- public void doUpdateVisitedHistory(WebView view, String url,
- boolean isReload) {
- 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)
- .concat(url.substring(end + 1));
- } else {
- // the url.charAt(index-1) should be either '?' or '&'
- url = url.substring(0, index-1);
- }
- }
- Browser.updateVisitedHistory(mResolver, url, true);
- WebIconDatabase.getInstance().retainIconForPageUrl(url);
- }
-
- /**
- * Displays SSL error(s) dialog to the user.
- */
- @Override
- public void onReceivedSslError(
- final WebView view, final SslErrorHandler handler, final SslError error) {
-
- if (mSettings.showSecurityWarnings()) {
- final LayoutInflater factory =
- LayoutInflater.from(BrowserActivity.this);
- final View warningsView =
- factory.inflate(R.layout.ssl_warnings, null);
- final LinearLayout placeholder =
- (LinearLayout)warningsView.findViewById(R.id.placeholder);
-
- if (error.hasError(SslError.SSL_UNTRUSTED)) {
- LinearLayout ll = (LinearLayout)factory
- .inflate(R.layout.ssl_warning, null);
- ((TextView)ll.findViewById(R.id.warning))
- .setText(R.string.ssl_untrusted);
- placeholder.addView(ll);
- }
-
- if (error.hasError(SslError.SSL_IDMISMATCH)) {
- LinearLayout ll = (LinearLayout)factory
- .inflate(R.layout.ssl_warning, null);
- ((TextView)ll.findViewById(R.id.warning))
- .setText(R.string.ssl_mismatch);
- placeholder.addView(ll);
- }
-
- if (error.hasError(SslError.SSL_EXPIRED)) {
- LinearLayout ll = (LinearLayout)factory
- .inflate(R.layout.ssl_warning, null);
- ((TextView)ll.findViewById(R.id.warning))
- .setText(R.string.ssl_expired);
- placeholder.addView(ll);
- }
-
- if (error.hasError(SslError.SSL_NOTYETVALID)) {
- LinearLayout ll = (LinearLayout)factory
- .inflate(R.layout.ssl_warning, null);
- ((TextView)ll.findViewById(R.id.warning))
- .setText(R.string.ssl_not_yet_valid);
- placeholder.addView(ll);
- }
-
- new AlertDialog.Builder(BrowserActivity.this)
- .setTitle(R.string.security_warning)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setView(warningsView)
- .setPositiveButton(R.string.ssl_continue,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- handler.proceed();
- }
- })
- .setNeutralButton(R.string.view_certificate,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- showSSLCertificateOnError(view, handler, error);
- }
- })
- .setNegativeButton(R.string.cancel,
- new DialogInterface.OnClickListener() {
- public void onClick(DialogInterface dialog, int whichButton) {
- handler.cancel();
- BrowserActivity.this.resetTitleAndRevertLockIcon();
- }
- })
- .setOnCancelListener(
- new DialogInterface.OnCancelListener() {
- public void onCancel(DialogInterface dialog) {
- handler.cancel();
- BrowserActivity.this.resetTitleAndRevertLockIcon();
- }
- })
- .show();
- } else {
- handler.proceed();
- }
+ // The "about:" schemes are internal to the browser; don't want these to
+ // be dispatched to other apps.
+ if (url.startsWith("about:")) {
+ return false;
}
- /**
- * Handles an HTTP authentication request.
- *
- * @param handler The authentication handler
- * @param host The host
- * @param realm The realm
- */
- @Override
- public void onReceivedHttpAuthRequest(WebView view,
- final HttpAuthHandler handler, final String host, final String realm) {
- String username = null;
- String password = null;
-
- boolean reuseHttpAuthUsernamePassword =
- handler.useHttpAuthUsernamePassword();
-
- if (reuseHttpAuthUsernamePassword &&
- (mTabControl.getCurrentWebView() != null)) {
- String[] credentials =
- mTabControl.getCurrentWebView()
- .getHttpAuthUsernamePassword(host, realm);
- if (credentials != null && credentials.length == 2) {
- username = credentials[0];
- password = credentials[1];
- }
- }
-
- if (username != null && password != null) {
- handler.proceed(username, password);
- } else {
- showHttpAuthentication(handler, host, realm, null, null, null, 0);
- }
+ Intent intent;
+ // perform generic parsing of the URI to turn it into an Intent.
+ try {
+ intent = Intent.parseUri(url, Intent.URI_INTENT_SCHEME);
+ } catch (URISyntaxException ex) {
+ Log.w("Browser", "Bad URI " + url + ": " + ex.getMessage());
+ return false;
}
- @Override
- public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event) {
- if (mMenuIsDown) {
- // only check shortcut key when MENU is held
- return getWindow().isShortcutKey(event.getKeyCode(), event);
+ // check whether the intent can be resolved. If not, we will see
+ // whether we can download it from the Market.
+ if (getPackageManager().resolveActivity(intent, 0) == null) {
+ String packagename = intent.getPackage();
+ if (packagename != null) {
+ intent = new Intent(Intent.ACTION_VIEW, Uri
+ .parse("market://search?q=pname:" + packagename));
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+ startActivity(intent);
+ return true;
} else {
return false;
}
}
- @Override
- public void onUnhandledKeyEvent(WebView view, KeyEvent event) {
- if (view != mTabControl.getCurrentTopWebView()) {
- return;
- }
- if (event.isDown()) {
- BrowserActivity.this.onKeyDown(event.getKeyCode(), event);
- } else {
- BrowserActivity.this.onKeyUp(event.getKeyCode(), event);
- }
- }
- };
-
- //--------------------------------------------------------------------------
- // WebChromeClient implementation
- //--------------------------------------------------------------------------
-
- /* package */ WebChromeClient getWebChromeClient() {
- return mWebChromeClient;
- }
-
- private final WebChromeClient mWebChromeClient = new WebChromeClient() {
- // Helper method to create a new tab or sub window.
- private void createWindow(final boolean dialog, final Message msg) {
- if (dialog) {
- mTabControl.createSubWindow();
- final TabControl.Tab t = mTabControl.getCurrentTab();
- attachSubWindow(t);
- WebView.WebViewTransport transport =
- (WebView.WebViewTransport) msg.obj;
- transport.setWebView(t.getSubWebView());
- msg.sendToTarget();
- } else {
- final TabControl.Tab parent = mTabControl.getCurrentTab();
- final TabControl.Tab newTab
- = openTabAndShow(EMPTY_URL_DATA, false, null);
- if (newTab != parent) {
- parent.addChildTab(newTab);
- }
- WebView.WebViewTransport transport =
- (WebView.WebViewTransport) msg.obj;
- transport.setWebView(mTabControl.getCurrentWebView());
- msg.sendToTarget();
- }
- }
-
- @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.
- if (dialog && mTabControl.getCurrentSubWindow() != null) {
- new AlertDialog.Builder(BrowserActivity.this)
- .setTitle(R.string.too_many_subwindows_dialog_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.too_many_subwindows_dialog_message)
- .setPositiveButton(R.string.ok, null)
- .show();
- return false;
- } else if (mTabControl.getTabCount() >= TabControl.MAX_TABS) {
- new AlertDialog.Builder(BrowserActivity.this)
- .setTitle(R.string.too_many_windows_dialog_title)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.too_many_windows_dialog_message)
- .setPositiveButton(R.string.ok, null)
- .show();
- return false;
- }
-
- // Short-circuit if this was a user gesture.
- if (userGesture) {
- createWindow(dialog, resultMsg);
+ // sanitize the Intent, ensuring web pages can not bypass browser
+ // security (only access to BROWSABLE activities).
+ intent.addCategory(Intent.CATEGORY_BROWSABLE);
+ intent.setComponent(null);
+ try {
+ if (startActivityIfNeeded(intent, -1)) {
return true;
}
-
- // Allow the popup and create the appropriate window.
- final AlertDialog.OnClickListener allowListener =
- new AlertDialog.OnClickListener() {
- public void onClick(DialogInterface d,
- int which) {
- createWindow(dialog, resultMsg);
- }
- };
-
- // Block the popup by returning a null WebView.
- final AlertDialog.OnClickListener blockListener =
- new AlertDialog.OnClickListener() {
- public void onClick(DialogInterface d, int which) {
- resultMsg.sendToTarget();
- }
- };
-
- // Build a confirmation dialog to display to the user.
- final AlertDialog d =
- new AlertDialog.Builder(BrowserActivity.this)
- .setTitle(R.string.attention)
- .setIcon(android.R.drawable.ic_dialog_alert)
- .setMessage(R.string.popup_window_attempt)
- .setPositiveButton(R.string.allow, allowListener)
- .setNegativeButton(R.string.block, blockListener)
- .setCancelable(false)
- .create();
-
- // Show the confirmation dialog.
- d.show();
- return true;
+ } catch (ActivityNotFoundException ex) {
+ // ignore the error. If no application can handle the URL,
+ // eg about:blank, assume the browser can handle it.
}
- @Override
- public void onCloseWindow(WebView window) {
- final TabControl.Tab current = mTabControl.getCurrentTab();
- final TabControl.Tab parent = current.getParentTab();
- if (parent != null) {
- // JavaScript can only close popup window.
- switchToTab(mTabControl.getTabIndex(parent));
- // Now we need to close the window
- closeTab(current);
- }
+ if (mMenuIsDown) {
+ openTab(url);
+ closeOptionsMenu();
+ return true;
}
+ return false;
+ }
- @Override
- public void onProgressChanged(WebView view, int newProgress) {
- mTitleBar.setProgress(newProgress);
- if (mFakeTitleBar != null) {
- mFakeTitleBar.setProgress(newProgress);
- }
+ // -------------------------------------------------------------------------
+ // Helper function for WebChromeClient
+ // -------------------------------------------------------------------------
- if (newProgress == 100) {
- // 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 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.
- mInLoad = true;
+ void onProgressChanged(WebView view, int newProgress) {
+ mFakeTitleBar.setProgress(newProgress);
+
+ if (newProgress == 100) {
+ // 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)
+ if (mInLoad) {
+ mInLoad = false;
updateInLoadMenuItems();
- if (!mOptionsMenuOpen || mIconView) {
- // This page has begun to load, so show the title bar
- showFakeTitleBar();
+ // If the options menu is open, leave the title bar
+ if (!mOptionsMenuOpen || !mIconView) {
+ hideFakeTitleBar();
}
}
- }
-
- @Override
- public void onReceivedTitle(WebView view, String title) {
- String url = view.getUrl();
-
- // here, if url is null, we want to reset the title
- setUrlTitle(url, title);
-
- if (url == null ||
- url.length() >= SQLiteDatabase.SQLITE_MAX_LIKE_PATTERN_LENGTH) {
- return;
- }
- // See if we can find the current url in our history database and
- // add the new title to it.
- if (url.startsWith("http://www.")) {
- url = url.substring(11);
- } else if (url.startsWith("http://")) {
- url = url.substring(4);
- }
- try {
- url = "%" + url;
- String [] selArgs = new String[] { url };
-
- String where = Browser.BookmarkColumns.URL + " LIKE ? AND "
- + Browser.BookmarkColumns.BOOKMARK + " = 0";
- Cursor c = mResolver.query(Browser.BOOKMARKS_URI,
- Browser.HISTORY_PROJECTION, where, selArgs, null);
- if (c.moveToFirst()) {
- // Current implementation of database only has one entry per
- // url.
- ContentValues map = new ContentValues();
- map.put(Browser.BookmarkColumns.TITLE, title);
- mResolver.update(Browser.BOOKMARKS_URI, map,
- "_id = " + c.getInt(0), null);
- }
- c.close();
- } catch (IllegalStateException e) {
- Log.e(LOGTAG, "BrowserActivity onReceived title", e);
- } catch (SQLiteException ex) {
- Log.e(LOGTAG, "onReceivedTitle() caught SQLiteException: ", ex);
+ } 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.
+ mInLoad = true;
+ updateInLoadMenuItems();
}
- }
-
- @Override
- public void onReceivedIcon(WebView view, Bitmap icon) {
- updateIcon(view, icon);
- }
-
- @Override
- 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) {
- // 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();
- }
+ // When the page first begins to load, the Activity may still be
+ // paused, in which case showFakeTitleBar will do nothing. Call
+ // again as the page continues to load so that it will be shown.
+ // (Calling it will the fake title bar is already showing will also
+ // do nothing.
+ if (!mOptionsMenuOpen || mIconView) {
+ // This page has begun to load, so show the title bar
+ showFakeTitleBar();
}
}
+ }
- @Override
- public void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
- if (mCustomView != null)
- return;
-
- // Add the custom view to its container.
- mCustomViewContainer.addView(view, COVER_SCREEN_GRAVITY_CENTER);
- mCustomView = view;
- mCustomViewCallback = callback;
- // Save the menu state and set it to empty while the custom
- // view is showing.
- mOldMenuState = mMenuState;
- mMenuState = EMPTY_MENU;
- // Hide the content view.
- mContentView.setVisibility(View.GONE);
- // Finally show the custom view container.
- mCustomViewContainer.setVisibility(View.VISIBLE);
- mCustomViewContainer.bringToFront();
- }
-
- @Override
- public void onHideCustomView() {
- if (mCustomView == null)
- return;
-
- // Hide the custom view.
- mCustomView.setVisibility(View.GONE);
- // Remove the custom view from its container.
- mCustomViewContainer.removeView(mCustomView);
- mCustomView = null;
- // Reset the old menu state.
- mMenuState = mOldMenuState;
- mOldMenuState = EMPTY_MENU;
- mCustomViewContainer.setVisibility(View.GONE);
- mCustomViewCallback.onCustomViewHidden();
- // Show the content view.
- mContentView.setVisibility(View.VISIBLE);
- }
-
- /**
- * The origin has exceeded its database quota.
- * @param url the URL that exceeded the quota
- * @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 estimatedSize,
- long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
- mSettings.getWebStorageSizeManager().onExceededDatabaseQuota(
- url, databaseIdentifier, currentQuota, estimatedSize,
- totalUsedQuota, quotaUpdater);
- }
-
- /**
- * The Application Cache has exceeded its max size.
- * @param spaceNeeded is the amount of disk space that would be needed
- * in order for the last appcache operation to succeed.
- * @param totalUsedQuota is the sum of all origins' quota.
- * @param quotaUpdater A callback to inform the WebCore thread that a new
- * app cache size is available. This callback must always be executed at
- * some point to ensure that the sleeping WebCore thread is woken up.
- */
- @Override
- public void onReachedMaxAppCacheSize(long spaceNeeded,
- long totalUsedQuota, WebStorage.QuotaUpdater quotaUpdater) {
- mSettings.getWebStorageSizeManager().onReachedMaxAppCacheSize(
- spaceNeeded, totalUsedQuota, quotaUpdater);
+ void onShowCustomView(View view, WebChromeClient.CustomViewCallback callback) {
+ // if a view already exists then immediately terminate the new one
+ if (mCustomView != null) {
+ callback.onCustomViewHidden();
+ return;
}
- /**
- * Instructs the browser to show a prompt to ask the user to set the
- * Geolocation permission state for the specified origin.
- * @param origin The origin for which Geolocation permissions are
- * requested.
- * @param callback The callback to call once the user has set the
- * Geolocation permission state.
- */
- @Override
- public void onGeolocationPermissionsShowPrompt(String origin,
- GeolocationPermissions.Callback callback) {
- mTabControl.getCurrentTab().getGeolocationPermissionsPrompt().show(
- origin, callback);
- }
+ // Add the custom view to its container.
+ mCustomViewContainer.addView(view, COVER_SCREEN_GRAVITY_CENTER);
+ mCustomView = view;
+ mCustomViewCallback = callback;
+ // Save the menu state and set it to empty while the custom
+ // view is showing.
+ mOldMenuState = mMenuState;
+ mMenuState = EMPTY_MENU;
+ // Hide the content view.
+ mContentView.setVisibility(View.GONE);
+ // Finally show the custom view container.
+ setStatusBarVisibility(false);
+ mCustomViewContainer.setVisibility(View.VISIBLE);
+ mCustomViewContainer.bringToFront();
+ }
+
+ void onHideCustomView() {
+ if (mCustomView == null)
+ return;
- /**
- * Instructs the browser to hide the Geolocation permissions prompt.
- */
- @Override
- public void onGeolocationPermissionsHidePrompt() {
- mTabControl.getCurrentTab().getGeolocationPermissionsPrompt().hide();
- }
+ // Hide the custom view.
+ mCustomView.setVisibility(View.GONE);
+ // Remove the custom view from its container.
+ mCustomViewContainer.removeView(mCustomView);
+ mCustomView = null;
+ // Reset the old menu state.
+ mMenuState = mOldMenuState;
+ mOldMenuState = EMPTY_MENU;
+ mCustomViewContainer.setVisibility(View.GONE);
+ mCustomViewCallback.onCustomViewHidden();
+ // Show the content view.
+ setStatusBarVisibility(true);
+ mContentView.setVisibility(View.VISIBLE);
+ }
- /* Adds a JavaScript error message to the system log and if the JS
- * console is enabled in the about:debug options, to that console
- * also.
- * @param message The error message to report.
- * @param lineNumber The line number of the error.
- * @param sourceID The name of the source file that caused the error.
- */
- @Override
- public void onConsoleMessage(String message, int lineNumber, String sourceID) {
- ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true);
- errorConsole.addErrorMessage(message, sourceID, lineNumber);
- if (mShouldShowErrorConsole &&
- errorConsole.getShowState() != ErrorConsoleView.SHOW_MAXIMIZED) {
- errorConsole.showConsole(ErrorConsoleView.SHOW_MINIMIZED);
- }
- Log.w(LOGTAG, "Console: " + message + " " + sourceID + ":" + lineNumber);
+ Bitmap getDefaultVideoPoster() {
+ if (mDefaultVideoPoster == null) {
+ mDefaultVideoPoster = BitmapFactory.decodeResource(
+ getResources(), R.drawable.default_video_poster);
}
+ return mDefaultVideoPoster;
+ }
- /**
- * 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;
+ View getVideoLoadingProgressView() {
+ if (mVideoProgressView == null) {
+ LayoutInflater inflater = LayoutInflater.from(BrowserActivity.this);
+ mVideoProgressView = inflater.inflate(
+ R.layout.video_loading_progress, null);
}
+ return mVideoProgressView;
+ }
- /**
- * Deliver a list of already-visited URLs
- * @hide pending API Council approval
- */
- @Override
- public void getVisitedHistory(final ValueCallback<String[]> callback) {
- AsyncTask<Void, Void, String[]> task = new AsyncTask<Void, Void, String[]>() {
- public String[] doInBackground(Void... unused) {
- return Browser.getVisitedHistory(getContentResolver());
- }
+ /*
+ * The Object used to inform the WebView of the file to upload.
+ */
+ private ValueCallback<Uri> mUploadMessage;
- public void onPostExecute(String[] result) {
- callback.onReceiveValue(result);
+ void openFileChooser(ValueCallback<Uri> uploadMsg) {
+ if (mUploadMessage != null) return;
+ mUploadMessage = uploadMsg;
+ Intent i = new Intent(Intent.ACTION_GET_CONTENT);
+ i.addCategory(Intent.CATEGORY_OPENABLE);
+ i.setType("*/*");
+ BrowserActivity.this.startActivityForResult(Intent.createChooser(i,
+ getString(R.string.choose_upload)), FILE_SELECTED);
+ }
- };
- };
- task.execute();
- };
- };
+ // -------------------------------------------------------------------------
+ // Implement functions for DownloadListener
+ // -------------------------------------------------------------------------
/**
* Notify the host application a download should be done, or that
onDownloadStartNoStream(url, userAgent, contentDisposition, mimetype, contentLength);
}
+ // This is to work around the fact that java.net.URI throws Exceptions
+ // instead of just encoding URL's properly
+ // Helper method for onDownloadStartNoStream
+ private static String encodePath(String path) {
+ char[] chars = path.toCharArray();
+
+ boolean needed = false;
+ for (char c : chars) {
+ if (c == '[' || c == ']') {
+ needed = true;
+ break;
+ }
+ }
+ if (needed == false) {
+ return path;
+ }
+
+ StringBuilder sb = new StringBuilder("");
+ for (char c : chars) {
+ if (c == '[' || c == ']') {
+ sb.append('%');
+ sb.append(Integer.toHexString(c));
+ } else {
+ sb.append(c);
+ }
+ }
+
+ return sb.toString();
+ }
+
/**
* Notify the host application a download should be done, even if there
* is a streaming viewer available for thise type.
return;
}
- // java.net.URI is a lot stricter than KURL so we have to undo
- // KURL's percent-encoding and redo the encoding using java.net.URI.
- URI uri = null;
+ // java.net.URI is a lot stricter than KURL so we have to encode some
+ // extra characters. Fix for b 2538060 and b 1634719
+ WebAddress webAddress;
try {
- // Undo the percent-encoding that KURL may have done.
- String newUrl = new String(URLUtil.decode(url.getBytes()));
- // Parse the url into pieces
- WebAddress w = new WebAddress(newUrl);
- String frag = null;
- String query = null;
- String path = w.mPath;
- // Break the path into path, query, and fragment
- if (path.length() > 0) {
- // Strip the fragment
- int idx = path.lastIndexOf('#');
- if (idx != -1) {
- frag = path.substring(idx + 1);
- path = path.substring(0, idx);
- }
- idx = path.lastIndexOf('?');
- if (idx != -1) {
- query = path.substring(idx + 1);
- path = path.substring(0, idx);
- }
- }
- uri = new URI(w.mScheme, w.mAuthInfo, w.mHost, w.mPort, path,
- query, frag);
+ webAddress = new WebAddress(url);
+ webAddress.mPath = encodePath(webAddress.mPath);
} catch (Exception e) {
- Log.e(LOGTAG, "Could not parse url for download: " + url, e);
+ // This only happens for very bad urls, we want to chatch the
+ // exception here
+ Log.e(LOGTAG, "Exception trying to parse url:" + url);
return;
}
String cookies = CookieManager.getInstance().getCookie(url);
ContentValues values = new ContentValues();
- values.put(Downloads.COLUMN_URI, uri.toString());
- values.put(Downloads.COLUMN_COOKIE_DATA, cookies);
- values.put(Downloads.COLUMN_USER_AGENT, userAgent);
- values.put(Downloads.COLUMN_NOTIFICATION_PACKAGE,
+ values.put(Downloads.Impl.COLUMN_URI, webAddress.toString());
+ values.put(Downloads.Impl.COLUMN_COOKIE_DATA, cookies);
+ values.put(Downloads.Impl.COLUMN_USER_AGENT, userAgent);
+ values.put(Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
getPackageName());
- values.put(Downloads.COLUMN_NOTIFICATION_CLASS,
+ values.put(Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
BrowserDownloadPage.class.getCanonicalName());
- values.put(Downloads.COLUMN_VISIBILITY, Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
- values.put(Downloads.COLUMN_MIME_TYPE, mimetype);
- values.put(Downloads.COLUMN_FILE_NAME_HINT, filename);
- values.put(Downloads.COLUMN_DESCRIPTION, uri.getHost());
+ values.put(Downloads.Impl.COLUMN_VISIBILITY,
+ Downloads.Impl.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
+ values.put(Downloads.Impl.COLUMN_MIME_TYPE, mimetype);
+ values.put(Downloads.Impl.COLUMN_FILE_NAME_HINT, filename);
+ values.put(Downloads.Impl.COLUMN_DESCRIPTION, webAddress.mHost);
if (contentLength > 0) {
- values.put(Downloads.COLUMN_TOTAL_BYTES, contentLength);
+ values.put(Downloads.Impl.COLUMN_TOTAL_BYTES, contentLength);
}
if (mimetype == null) {
// We must have long pressed on a link or image to download it. We
new FetchUrlMimeType(this).execute(values);
} else {
final Uri contentUri =
- getContentResolver().insert(Downloads.CONTENT_URI, values);
- viewDownloads(contentUri);
+ getContentResolver().insert(Downloads.Impl.CONTENT_URI, values);
}
-
+ Toast.makeText(this, R.string.download_pending, Toast.LENGTH_SHORT)
+ .show();
}
+ // -------------------------------------------------------------------------
+
/**
* Resets the lock icon. This method is called when we start a new load and
* know the url to be loaded.
*/
private void resetLockIcon(String url) {
// Save the lock-icon state (we revert to it if the load gets cancelled)
- saveLockIcon();
-
- mLockIconType = LOCK_ICON_UNSECURE;
- if (URLUtil.isHttpsUrl(url)) {
- mLockIconType = LOCK_ICON_SECURE;
- if (LOGV_ENABLED) {
- Log.v(LOGTAG, "BrowserActivity.resetLockIcon:" +
- " reset lock icon to " + mLockIconType);
- }
- }
-
+ mTabControl.getCurrentTab().resetLockIcon(url);
updateLockIconImage(LOCK_ICON_UNSECURE);
}
- /* package */ void setLockIconType(int type) {
- mLockIconType = type;
- }
-
- /* package */ int getLockIconType() {
- return mLockIconType;
- }
-
- /* package */ void setPrevLockType(int type) {
- mPrevLockType = type;
- }
-
- /* package */ int getPrevLockType() {
- return mPrevLockType;
- }
-
/**
* Update the lock icon to correspond to our latest state.
*/
- /* package */ void updateLockIconToLatest() {
- updateLockIconImage(mLockIconType);
+ private void updateLockIconToLatest() {
+ updateLockIconImage(mTabControl.getCurrentTab().getLockIconType());
}
/**
d = mMixLockIcon;
}
mTitleBar.setLock(d);
- if (mFakeTitleBar != null) {
- mFakeTitleBar.setLock(d);
- }
+ mFakeTitleBar.setLock(d);
}
/**
* not. This is important, since we need to know whether to return to
* the parent dialog or simply dismiss.
*/
- private void showPageInfo(final TabControl.Tab tab,
+ private void showPageInfo(final Tab tab,
final boolean fromShowSSLCertificateOnError) {
final LayoutInflater factory = LayoutInflater
.from(this);
((TextView) pageInfoView.findViewById(R.id.title)).setText(title);
mPageInfoView = tab;
- mPageInfoFromShowSSLCertificateOnError = new Boolean(fromShowSSLCertificateOnError);
+ mPageInfoFromShowSSLCertificateOnError = fromShowSSLCertificateOnError;
AlertDialog.Builder alertDialogBuilder =
new AlertDialog.Builder(this)
int whichButton) {
mPageInfoDialog = null;
mPageInfoView = null;
- mPageInfoFromShowSSLCertificateOnError = null;
// if we came here from the SSL error dialog
if (fromShowSSLCertificateOnError) {
public void onCancel(DialogInterface dialog) {
mPageInfoDialog = null;
mPageInfoView = null;
- mPageInfoFromShowSSLCertificateOnError = null;
// if we came here from the SSL error dialog
if (fromShowSSLCertificateOnError) {
int whichButton) {
mPageInfoDialog = null;
mPageInfoView = null;
- mPageInfoFromShowSSLCertificateOnError = null;
// if we came here from the SSL error dialog
if (fromShowSSLCertificateOnError) {
* (accessible from the Page-Info dialog).
* @param tab The tab to show certificate for.
*/
- private void showSSLCertificate(final TabControl.Tab tab) {
+ private void showSSLCertificate(final Tab tab) {
final View certificateView =
inflateCertificateView(tab.getWebView().getCertificate());
if (certificateView == null) {
* connection that resulted in an SSL error or proceeding per user request.
* @param error The SSL error object.
*/
- private void showSSLCertificateOnError(
+ void showSSLCertificateOnError(
final WebView view, final SslErrorHandler handler, final SslError error) {
final View certificateView =
mSSLCertificateOnErrorHandler = null;
mSSLCertificateOnErrorError = null;
- mWebViewClient.onReceivedSslError(
- view, handler, error);
+ view.getWebViewClient().onReceivedSslError(
+ view, handler, error);
}
})
.setNeutralButton(R.string.page_info_view,
mSSLCertificateOnErrorHandler = null;
mSSLCertificateOnErrorError = null;
- mWebViewClient.onReceivedSslError(
- view, handler, error);
+ view.getWebViewClient().onReceivedSslError(
+ view, handler, error);
}
})
.show();
}
// issued on:
- String issuedOn = reformatCertificateDate(
- certificate.getValidNotBefore());
+ String issuedOn = formatCertificateDate(
+ certificate.getValidNotBeforeDate());
((TextView) certificateView.findViewById(R.id.issued_on))
.setText(issuedOn);
// expires on:
- String expiresOn = reformatCertificateDate(
- certificate.getValidNotAfter());
+ String expiresOn = formatCertificateDate(
+ certificate.getValidNotAfterDate());
((TextView) certificateView.findViewById(R.id.expires_on))
.setText(expiresOn);
}
/**
- * Re-formats the certificate date (Date.toString()) string to
- * a properly localized date string.
+ * Formats the certificate date to a properly localized date string.
* @return Properly localized version of the certificate date string and
- * the original certificate date string if fails to localize.
- * If the original string is null, returns an empty string "".
+ * the "" if it fails to localize.
*/
- private String reformatCertificateDate(String certificateDate) {
- String reformattedDate = null;
-
- if (certificateDate != null) {
- Date date = null;
- try {
- date = java.text.DateFormat.getInstance().parse(certificateDate);
- } catch (ParseException e) {
- date = null;
- }
-
- if (date != null) {
- reformattedDate =
- DateFormat.getDateFormat(this).format(date);
- }
+ private String formatCertificateDate(Date certificateDate) {
+ if (certificateDate == null) {
+ return "";
}
-
- return reformattedDate != null ? reformattedDate :
- (certificateDate != null ? certificateDate : "");
+ String formattedDate = DateFormat.getDateFormat(this).format(certificateDate);
+ if (formattedDate == null) {
+ return "";
+ }
+ return formattedDate;
}
/**
* Displays an http-authentication dialog.
*/
- private void showHttpAuthentication(final HttpAuthHandler handler,
+ void showHttpAuthentication(final HttpAuthHandler handler,
final String host, final String realm, final String title,
final String name, final String password, int focusId) {
LayoutInflater factory = LayoutInflater.from(this);
public void setHttpAuthUsernamePassword(String host, String realm,
String username,
String password) {
- WebView w = mTabControl.getCurrentWebView();
+ WebView w = getTopWindow();
if (w != null) {
w.setHttpAuthUsernamePassword(host, realm, username, password);
}
}
}
+ boolean isNetworkUp() {
+ return mIsNetworkUp;
+ }
+
// 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() {
@Override
protected void onActivityResult(int requestCode, int resultCode,
Intent intent) {
+ if (getTopWindow() == null) return;
+
switch (requestCode) {
case COMBO_PAGE:
if (resultCode == RESULT_OK && intent != null) {
if (extras != null && extras.getBoolean("new_window", false)) {
openTab(data);
} else {
- final TabControl.Tab currentTab =
+ final Tab currentTab =
mTabControl.getCurrentTab();
dismissSubWindow(currentTab);
if (data != null && data.length() != 0) {
- getTopWindow().loadUrl(data);
+ loadUrl(getTopWindow(), data);
}
}
}
+ // Deliberately fall through to PREFERENCES_PAGE, since the
+ // same extra may be attached to the COMBO_PAGE
+ case PREFERENCES_PAGE:
+ if (resultCode == RESULT_OK && intent != null) {
+ String action = intent.getStringExtra(Intent.EXTRA_TEXT);
+ if (BrowserSettings.PREF_CLEAR_HISTORY.equals(action)) {
+ mTabControl.removeParentChildRelationShips();
+ }
+ }
+ break;
+ // Choose a file from the file picker.
+ case FILE_SELECTED:
+ if (null == mUploadMessage) break;
+ Uri result = intent == null || resultCode != RESULT_OK ? null
+ : intent.getData();
+ mUploadMessage.onReceiveValue(result);
+ mUploadMessage = null;
break;
default:
break;
/*
* This method is called as a result of the user selecting the options
- * menu to see the download window, or when a download changes state. It
- * shows the download window ontop of the current window.
+ * menu to see the download window. It shows the download window on top of
+ * the current window.
*/
- /* package */ void viewDownloads(Uri downloadRecord) {
+ private void viewDownloads(Uri downloadRecord) {
Intent intent = new Intent(this,
BrowserDownloadPage.class);
intent.setData(downloadRecord);
- startActivityForResult(intent, this.DOWNLOAD_PAGE);
+ startActivityForResult(intent, BrowserActivity.DOWNLOAD_PAGE);
}
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);
+ intent.putExtra("disable_new_window", !mTabControl.canCreateNewTab());
intent.putExtra("touch_icon_url", current.getTouchIconUrl());
if (startWithHistory) {
intent.putExtra(CombinedBookmarkHistoryActivity.STARTING_TAB,
}
// Called when loading from context menu or LOAD_URL message
- private void loadURL(WebView view, String url) {
+ private void loadUrlFromContext(WebView view, String url) {
// In case the user enters nothing.
if (url != null && url.length() != 0 && view != null) {
url = smartUrlFilter(url);
- if (!mWebViewClient.shouldOverrideUrlLoading(view, url)) {
- view.loadUrl(url);
+ if (!view.getWebViewClient().shouldOverrideUrlLoading(view, url)) {
+ loadUrl(view, url);
}
}
}
+ /**
+ * Load the URL into the given WebView and update the title bar
+ * to reflect the new load. Call this instead of WebView.loadUrl
+ * directly.
+ * @param view The WebView used to load url.
+ * @param url The URL to load.
+ */
+ private void loadUrl(WebView view, String url) {
+ updateTitleBarForNewLoad(view, url);
+ view.loadUrl(url);
+ }
+
+ /**
+ * Load UrlData into a Tab and update the title bar to reflect the new
+ * load. Call this instead of UrlData.loadIn directly.
+ * @param t The Tab used to load.
+ * @param data The UrlData being loaded.
+ */
+ private void loadUrlDataIn(Tab t, UrlData data) {
+ updateTitleBarForNewLoad(t.getWebView(), data.mUrl);
+ data.loadIn(t);
+ }
+
+ /**
+ * If the WebView is the top window, update the title bar to reflect
+ * loading the new URL. i.e. set its text, clear the favicon (which
+ * will be set once the page begins loading), and set the progress to
+ * INITIAL_PROGRESS to show that the page has begun to load. Called
+ * by loadUrl and loadUrlDataIn.
+ * @param view The WebView that is starting a load.
+ * @param url The URL that is being loaded.
+ */
+ private void updateTitleBarForNewLoad(WebView view, String url) {
+ if (view == getTopWindow()) {
+ setUrlTitle(url, null);
+ setFavicon(null);
+ onProgressChanged(view, INITIAL_PROGRESS);
+ }
+ }
+
private String smartUrlFilter(Uri inUri) {
if (inUri != null) {
return smartUrlFilter(inUri.toString());
return null;
}
-
- // get window count
-
- int getWindowCount(){
- if(mTabControl != null){
- return mTabControl.getTabCount();
- }
- return 0;
- }
-
protected static final Pattern ACCEPTED_URI_SCHEMA = Pattern.compile(
"(?i)" + // switch on case insensitive matching
"(" + // begin group for schema
}
}
} else {
- if (Regex.WEB_URL_PATTERN.matcher(inUrl).matches()) {
+ if (Patterns.WEB_URL.matcher(inUrl).matches()) {
return URLUtil.guessUrl(inUrl);
}
}
mShouldShowErrorConsole = flag;
- ErrorConsoleView errorConsole = mTabControl.getCurrentErrorConsole(true);
+ ErrorConsoleView errorConsole = mTabControl.getCurrentTab()
+ .getErrorConsole(true);
if (flag) {
// Setting the show state of the console will cause it's the layout to be inflated.
// Now we can add it to the main view.
mErrorConsoleContainer.addView(errorConsole,
- new LinearLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
+ new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
} else {
mErrorConsoleContainer.removeView(errorConsole);
}
+ boolean shouldShowErrorConsole() {
+ return mShouldShowErrorConsole;
+ }
+
+ private void setStatusBarVisibility(boolean visible) {
+ int flag = visible ? 0 : WindowManager.LayoutParams.FLAG_FULLSCREEN;
+ getWindow().setFlags(flag, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ }
+
+
+ private void sendNetworkType(String type, String subtype) {
+ WebView w = mTabControl.getCurrentWebView();
+ if (w != null) {
+ w.setNetworkType(type, subtype);
+ }
+ }
+
+ private void packageChanged(String packageName, boolean wasAdded) {
+ WebView w = mTabControl.getCurrentWebView();
+ if (w == null) {
+ return;
+ }
+
+ if (wasAdded) {
+ w.addPackageName(packageName);
+ } else {
+ w.removePackageName(packageName);
+ }
+ }
+
+ private void addPackageNames(Set<String> packageNames) {
+ WebView w = mTabControl.getCurrentWebView();
+ if (w == null) {
+ return;
+ }
+
+ w.addPackageNames(packageNames);
+ }
+
+ private void getInstalledPackages() {
+ AsyncTask<Void, Void, Set<String> > task =
+ new AsyncTask<Void, Void, Set<String> >() {
+ protected Set<String> doInBackground(Void... unused) {
+ Set<String> installedPackages = new HashSet<String>();
+ PackageManager pm = BrowserActivity.this.getPackageManager();
+ if (pm != null) {
+ List<PackageInfo> packages = pm.getInstalledPackages(0);
+ for (PackageInfo p : packages) {
+ if (BrowserActivity.this.sGoogleApps.contains(p.packageName)) {
+ installedPackages.add(p.packageName);
+ }
+ }
+ }
+
+ return installedPackages;
+ }
+
+ // Executes on the UI thread
+ protected void onPostExecute(Set<String> installedPackages) {
+ addPackageNames(installedPackages);
+ }
+ };
+ task.execute();
+ }
+
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 BrowserSettings mSettings;
private TabControl mTabControl;
private ContentResolver mResolver;
private boolean mIsNetworkUp;
private boolean mDidStopLoad;
- private boolean mPageStarted;
- private boolean mActivityInPause = true;
+ /* package */ boolean mActivityInPause = true;
private boolean mMenuIsDown;
/* hold a ref so we can auto-cancel if necessary */
private AlertDialog mAlertDialog;
- // Wait for credentials before loading google.com
- private ProgressDialog mCredsDlg;
-
// The up-to-date URL and title (these can be different from those stored
// in WebView, since it takes some time for the information in WebView to
// get updated)
// As PageInfo has different style for landscape / portrait, we have
// to re-open it when configuration changed
private AlertDialog mPageInfoDialog;
- private TabControl.Tab mPageInfoView;
+ private Tab mPageInfoView;
// If the Page-Info dialog is launched from the SSL-certificate-on-error
// dialog, we should not just dismiss it, but should get back to the
// SSL-certificate-on-error dialog. This flag is used to store this state
- private Boolean mPageInfoFromShowSSLCertificateOnError;
+ private boolean mPageInfoFromShowSSLCertificateOnError;
// as SSLCertificateOnError has different style for landscape / portrait,
// we have to re-open it when configuration changed
// as SSLCertificate has different style for landscape / portrait, we
// have to re-open it when configuration changed
private AlertDialog mSSLCertificateDialog;
- private TabControl.Tab mSSLCertificateView;
+ private Tab mSSLCertificateView;
// as HttpAuthentication has different style for landscape / portrait, we
// have to re-open it when configuration changed
/*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_PARAMS =
new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.FILL_PARENT);
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
/*package*/ static final FrameLayout.LayoutParams COVER_SCREEN_GRAVITY_CENTER =
new FrameLayout.LayoutParams(
- ViewGroup.LayoutParams.FILL_PARENT,
- ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT,
Gravity.CENTER);
// Google search
final static String QuickSearch_G = "http://www.google.com/m?q=%s";
private BroadcastReceiver mPackageInstallationReceiver;
- // AsyncTask for downloading touch icons
- /* package */ DownloadTouchIcon mTouchIconLoader;
+ private SystemAllowGeolocationOrigins mSystemAllowGeolocationOrigins;
// activity requestCode
final static int COMBO_PAGE = 1;
final static int DOWNLOAD_PAGE = 2;
final static int PREFERENCES_PAGE = 3;
+ final static int FILE_SELECTED = 4;
// the default <video> poster
private Bitmap mDefaultVideoPoster;
// the video progress view
private View mVideoProgressView;
+ // The Google packages we monitor for the navigator.isApplicationInstalled()
+ // API. Add as needed.
+ private static Set<String> sGoogleApps;
+ static {
+ sGoogleApps = new HashSet<String>();
+ sGoogleApps.add("com.google.android.youtube");
+ }
+
/**
* A UrlData class to abstract how the content will be set to WebView.
* This base class uses loadUrl to show the content.
*/
- private static class UrlData {
- String mUrl;
- byte[] mPostData;
+ /* package */ static class UrlData {
+ final String mUrl;
+ final Map<String, String> mHeaders;
+ final Intent mVoiceIntent;
UrlData(String url) {
this.mUrl = url;
+ this.mHeaders = null;
+ this.mVoiceIntent = null;
}
- void setPostData(byte[] postData) {
- mPostData = postData;
+ UrlData(String url, Map<String, String> headers, Intent intent) {
+ this.mUrl = url;
+ this.mHeaders = headers;
+ if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS
+ .equals(intent.getAction())) {
+ this.mVoiceIntent = intent;
+ } else {
+ this.mVoiceIntent = null;
+ }
}
boolean isEmpty() {
- return mUrl == null || mUrl.length() == 0;
+ return mVoiceIntent == null && (mUrl == null || mUrl.length() == 0);
}
- public void loadIn(WebView webView) {
- if (mPostData != null) {
- webView.postUrl(mUrl, mPostData);
+ /**
+ * Load this UrlData into the given Tab. Use loadUrlDataIn to update
+ * the title bar as well.
+ */
+ public void loadIn(Tab t) {
+ if (mVoiceIntent != null) {
+ t.activateVoiceSearchMode(mVoiceIntent);
} else {
- webView.loadUrl(mUrl);
+ t.getWebView().loadUrl(mUrl, mHeaders);
}
}
};
- /**
- * A subclass of UrlData class that can display inlined content using
- * {@link WebView#loadDataWithBaseURL(String, String, String, String, String)}.
- */
- private static class InlinedUrlData extends UrlData {
- InlinedUrlData(String inlined, String mimeType, String encoding, String failUrl) {
- super(failUrl);
- mInlined = inlined;
- mMimeType = mimeType;
- mEncoding = encoding;
- }
- String mMimeType;
- String mInlined;
- String mEncoding;
- @Override
- boolean isEmpty() {
- return mInlined == null || mInlined.length() == 0 || super.isEmpty();
- }
-
- @Override
- public void loadIn(WebView webView) {
- webView.loadDataWithBaseURL(null, mInlined, mMimeType, mEncoding, mUrl);
- }
- }
-
/* package */ static final UrlData EMPTY_URL_DATA = new UrlData(null);
}