summaryrefslogtreecommitdiffstats
path: root/src/com/android
diff options
context:
space:
mode:
authorMichael Kolb <kolby@google.com>2011-06-24 13:06:29 -0700
committerMichael Kolb <kolby@google.com>2011-06-30 12:00:49 -0700
commit1461244018a225006a8d4c203f9dfe294ffe94fa (patch)
tree3dd496c312755b5f0560c36fc3be4dffa639197a /src/com/android
parent4399b13ca463824bbab1ba422f4004cff100b483 (diff)
downloadandroid_packages_apps_Gello-1461244018a225006a8d4c203f9dfe294ffe94fa.tar.gz
android_packages_apps_Gello-1461244018a225006a8d4c203f9dfe294ffe94fa.tar.bz2
android_packages_apps_Gello-1461244018a225006a8d4c203f9dfe294ffe94fa.zip
Preloading support in browser
Apps like the QSB can request the browser to preload a web page. - preloaded pages are not added to the browser history if they'r not seen by the user - when a request is received, a new tab is created for the preloaded page, but not added to the tab list - upon receiving the view intent for the preloaded page the tab is added to the tab list, and shown - if several pages are preloaded consecutively in the same tab, the back stack is cleared before it is displayed - preloaded pages use the main browser cookie jar, so pages that have never been viewed by the user can drop cookies Change-Id: I9ed21f2c9560fda0ed042b460b73bb33988a2e8a
Diffstat (limited to 'src/com/android')
-rw-r--r--src/com/android/browser/BaseUi.java37
-rw-r--r--src/com/android/browser/Browser.java1
-rw-r--r--src/com/android/browser/BrowserActivity.java1
-rw-r--r--src/com/android/browser/BrowserSettings.java7
-rw-r--r--src/com/android/browser/BrowserWebViewFactory.java69
-rw-r--r--src/com/android/browser/Controller.java31
-rw-r--r--src/com/android/browser/IntentHandler.java34
-rw-r--r--src/com/android/browser/PreferenceKeys.java5
-rw-r--r--src/com/android/browser/PreloadController.java224
-rw-r--r--src/com/android/browser/PreloadRequestReceiver.java82
-rw-r--r--src/com/android/browser/Preloader.java137
-rw-r--r--src/com/android/browser/SnapshotTab.java2
-rw-r--r--src/com/android/browser/Tab.java71
-rw-r--r--src/com/android/browser/TabControl.java8
-rw-r--r--src/com/android/browser/WebViewController.java5
-rw-r--r--src/com/android/browser/XLargeUi.java23
-rw-r--r--src/com/android/browser/preferences/BandwidthPreferencesFragment.java39
17 files changed, 678 insertions, 98 deletions
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java
index d3643785..1836e6e2 100644
--- a/src/com/android/browser/BaseUi.java
+++ b/src/com/android/browser/BaseUi.java
@@ -60,7 +60,7 @@ import java.util.List;
/**
* UI interface definitions
*/
-public abstract class BaseUi implements UI, WebViewFactory, OnTouchListener {
+public abstract class BaseUi implements UI, OnTouchListener {
private static final String LOGTAG = "BaseUi";
@@ -145,41 +145,6 @@ public abstract class BaseUi implements UI, WebViewFactory, OnTouchListener {
config.getScaledTouchSlop());
}
- @Override
- public WebView createWebView(boolean privateBrowsing) {
- // Create a new WebView
- BrowserWebView w = new BrowserWebView(mActivity, null,
- android.R.attr.webViewStyle, privateBrowsing);
- initWebViewSettings(w);
- return w;
- }
-
- @Override
- public WebView createSubWebView(boolean privateBrowsing) {
- return createWebView(privateBrowsing);
- }
-
- /**
- * common webview initialization
- * @param w the webview to initialize
- */
- protected void initWebViewSettings(WebView w) {
- w.setScrollbarFadingEnabled(true);
- w.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
- w.setMapTrackballToArrowKeys(false); // use trackball directly
- // Enable the built-in zoom
- w.getSettings().setBuiltInZoomControls(true);
- boolean supportsMultiTouch = mActivity.getPackageManager()
- .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH);
- w.getSettings().setDisplayZoomControls(!supportsMultiTouch);
- w.setExpandedTileBounds(true); // smoother scrolling
-
- // Add this WebView to the settings observer list and update the
- // settings
- final BrowserSettings s = BrowserSettings.getInstance();
- s.startManagingSettings(w.getSettings());
- }
-
private void cancelStopToast() {
if (mStopToast != null) {
mStopToast.cancel();
diff --git a/src/com/android/browser/Browser.java b/src/com/android/browser/Browser.java
index 65eb0ce3..909a50d7 100644
--- a/src/com/android/browser/Browser.java
+++ b/src/com/android/browser/Browser.java
@@ -58,6 +58,7 @@ public class Browser extends Application {
// create CookieSyncManager with current Context
CookieSyncManager.createInstance(this);
BrowserSettings.initialize(getApplicationContext());
+ Preloader.initialize(getApplicationContext());
}
static Intent createBrowserViewIntent() {
diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java
index dbcae2e3..13b8b06b 100644
--- a/src/com/android/browser/BrowserActivity.java
+++ b/src/com/android/browser/BrowserActivity.java
@@ -92,7 +92,6 @@ public class BrowserActivity extends Activity {
mUi = new PhoneUi(this, mController);
}
mController.setUi(mUi);
- mController.setWebViewFactory((BaseUi) mUi);
Bundle state = getIntent().getBundleExtra(EXTRA_STATE);
if (state != null && icicle == null) {
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 4928e615..3a6349a5 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -689,4 +689,11 @@ public class BrowserSettings implements OnSharedPreferenceChangeListener,
return mPrefs.getBoolean(PREF_REMEMBER_PASSWORDS, true);
}
+ // -----------------------------
+ // getter/setters for bandwidth_preferences.xml
+ // -----------------------------
+
+ public boolean isPreloadEnabled() {
+ return mPrefs.getBoolean(PREF_DATA_PRELOAD, false);
+ }
}
diff --git a/src/com/android/browser/BrowserWebViewFactory.java b/src/com/android/browser/BrowserWebViewFactory.java
new file mode 100644
index 00000000..fbd26a94
--- /dev/null
+++ b/src/com/android/browser/BrowserWebViewFactory.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.browser;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.util.AttributeSet;
+import android.view.View;
+import android.webkit.WebView;
+
+/**
+ * Web view factory class for creating {@link BrowserWebView}'s.
+ */
+public class BrowserWebViewFactory implements WebViewFactory {
+
+ private final Context mContext;
+
+ public BrowserWebViewFactory(Context context) {
+ mContext = context;
+ }
+
+ protected WebView instantiateWebView(AttributeSet attrs, int defStyle,
+ boolean privateBrowsing) {
+ return new BrowserWebView(mContext, attrs, defStyle, privateBrowsing);
+ }
+
+ @Override
+ public WebView createSubWebView(boolean privateBrowsing) {
+ return createWebView(privateBrowsing);
+ }
+
+ @Override
+ public WebView createWebView(boolean privateBrowsing) {
+ WebView w = instantiateWebView(null, android.R.attr.webViewStyle, privateBrowsing);
+ initWebViewSettings(w);
+ return w;
+ }
+
+ protected void initWebViewSettings(WebView w) {
+ w.setScrollbarFadingEnabled(true);
+ w.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_OVERLAY);
+ w.setMapTrackballToArrowKeys(false); // use trackball directly
+ // Enable the built-in zoom
+ w.getSettings().setBuiltInZoomControls(true);
+ boolean supportsMultiTouch = mContext.getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH);
+ w.getSettings().setDisplayZoomControls(!supportsMultiTouch);
+ w.setExpandedTileBounds(true); // smoother scrolling
+
+ // Add this WebView to the settings observer list and update the
+ // settings
+ final BrowserSettings s = BrowserSettings.getInstance();
+ s.startManagingSettings(w.getSettings());
+ }
+
+}
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 447e61b2..9046745f 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -229,6 +229,7 @@ public class Controller
mTabControl = new TabControl(this);
mSettings.setController(this);
mCrashRecoveryHandler = CrashRecoveryHandler.initialize(this);
+ mFactory = new BrowserWebViewFactory(browser);
mUrlHandler = new UrlHandler(this);
mIntentHandler = new IntentHandler(mActivity, this);
@@ -312,7 +313,7 @@ public class Controller
// If the intent is ACTION_VIEW and data is not null, the Browser is
// invoked to view the content by another application. In this case,
// the tab will be close when exit.
- UrlData urlData = mIntentHandler.getUrlDataFromIntent(intent);
+ UrlData urlData = IntentHandler.getUrlDataFromIntent(intent);
Tab t = null;
if (urlData.isEmpty()) {
t = openTabToHomePage();
@@ -356,10 +357,6 @@ public class Controller
}
}
- void setWebViewFactory(WebViewFactory factory) {
- mFactory = factory;
- }
-
@Override
public WebViewFactory getWebViewFactory() {
return mFactory;
@@ -381,6 +378,11 @@ public class Controller
}
@Override
+ public Context getContext() {
+ return mActivity;
+ }
+
+ @Override
public Activity getActivity() {
return mActivity;
}
@@ -1637,6 +1639,7 @@ public class Controller
return id;
}
+ @Override
protected void onPostExecute(Long id) {
if (id > 0) {
createNewSnapshotTab(id, true);
@@ -2247,11 +2250,19 @@ public class Controller
// open a non inconito tab with the given url data
// and set as active tab
public Tab openTab(UrlData urlData) {
- Tab tab = createNewTab(false, true, true);
- if ((tab != null) && !urlData.isEmpty()) {
- loadUrlDataIn(tab, urlData);
+ if (urlData.isPreloaded()) {
+ Tab tab = urlData.getPreloadedTab();
+ tab.getWebView().clearHistory();
+ mTabControl.addPreloadedTab(tab);
+ setActiveTab(tab);
+ return tab;
+ } else {
+ Tab tab = createNewTab(false, true, true);
+ if ((tab != null) && !urlData.isEmpty()) {
+ loadUrlDataIn(tab, urlData);
+ }
+ return tab;
}
- return tab;
}
@Override
@@ -2417,6 +2428,8 @@ public class Controller
if (data != null) {
if (data.mVoiceIntent != null) {
t.activateVoiceSearchMode(data.mVoiceIntent);
+ } else if (data.isPreloaded()) {
+ // this isn't called for preloaded tabs
} else {
loadUrl(t, data.mUrl, data.mHeaders);
}
diff --git a/src/com/android/browser/IntentHandler.java b/src/com/android/browser/IntentHandler.java
index 088a788d..a99164a4 100644
--- a/src/com/android/browser/IntentHandler.java
+++ b/src/com/android/browser/IntentHandler.java
@@ -135,8 +135,14 @@ public class IntentHandler {
urlData = new UrlData(mSettings.getHomePage());
}
- if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)) {
- mController.openTab(urlData);
+ if (intent.getBooleanExtra(Browser.EXTRA_CREATE_NEW_TAB, false)
+ || urlData.isPreloaded()) {
+ Tab t = mController.openTab(urlData);
+ if (t == null && urlData.isPreloaded()) {
+ Tab pre = urlData.getPreloadedTab();
+ // TODO: check if we need to stop loading
+ pre.destroy();
+ }
return;
}
/*
@@ -220,9 +226,10 @@ public class IntentHandler {
}
}
- protected UrlData getUrlDataFromIntent(Intent intent) {
+ protected static UrlData getUrlDataFromIntent(Intent intent) {
String url = "";
Map<String, String> headers = null;
+ Tab preloaded = null;
if (intent != null
&& (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
final String action = intent.getAction();
@@ -241,6 +248,10 @@ public class IntentHandler {
}
}
}
+ if (intent.hasExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID)) {
+ String id = intent.getStringExtra(PreloadRequestReceiver.EXTRA_PRELOAD_ID);
+ preloaded = Preloader.getInstance().getPreloadedTab(id);
+ }
} else if (Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
|| Intent.ACTION_WEB_SEARCH.equals(action)) {
@@ -265,7 +276,7 @@ public class IntentHandler {
}
}
}
- return new UrlData(url, headers, intent);
+ return new UrlData(url, headers, intent, preloaded);
}
/**
@@ -348,14 +359,20 @@ public class IntentHandler {
final String mUrl;
final Map<String, String> mHeaders;
final Intent mVoiceIntent;
+ final Tab mPreloadedTab;
UrlData(String url) {
this.mUrl = url;
this.mHeaders = null;
this.mVoiceIntent = null;
+ this.mPreloadedTab = null;
}
UrlData(String url, Map<String, String> headers, Intent intent) {
+ this(url, headers, intent, null);
+ }
+
+ UrlData(String url, Map<String, String> headers, Intent intent, Tab preloaded) {
this.mUrl = url;
this.mHeaders = headers;
if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS
@@ -364,11 +381,20 @@ public class IntentHandler {
} else {
this.mVoiceIntent = null;
}
+ mPreloadedTab = preloaded;
}
boolean isEmpty() {
return mVoiceIntent == null && (mUrl == null || mUrl.length() == 0);
}
+
+ boolean isPreloaded() {
+ return mPreloadedTab != null;
+ }
+
+ Tab getPreloadedTab() {
+ return mPreloadedTab;
+ }
}
}
diff --git a/src/com/android/browser/PreferenceKeys.java b/src/com/android/browser/PreferenceKeys.java
index bc8d38f7..144d5054 100644
--- a/src/com/android/browser/PreferenceKeys.java
+++ b/src/com/android/browser/PreferenceKeys.java
@@ -94,4 +94,9 @@ public interface PreferenceKeys {
static final String PREF_SAVE_FORMDATA = "save_formdata";
static final String PREF_SHOW_SECURITY_WARNINGS = "show_security_warnings";
+ // ----------------------
+ // Keys for bandwidth_preferences.xml
+ // ----------------------
+ static final String PREF_DATA_PRELOAD = "preload_enabled";
+
}
diff --git a/src/com/android/browser/PreloadController.java b/src/com/android/browser/PreloadController.java
new file mode 100644
index 00000000..65284107
--- /dev/null
+++ b/src/com/android/browser/PreloadController.java
@@ -0,0 +1,224 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.browser;
+
+import android.app.Activity;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.net.http.SslError;
+import android.os.Message;
+import android.view.KeyEvent;
+import android.view.View;
+import android.webkit.HttpAuthHandler;
+import android.webkit.SslErrorHandler;
+import android.webkit.ValueCallback;
+import android.webkit.WebChromeClient.CustomViewCallback;
+import android.webkit.WebView;
+
+import java.util.List;
+
+public class PreloadController implements WebViewController {
+
+ private Context mContext;
+
+ public PreloadController(Context ctx) {
+ mContext = ctx;
+
+ }
+
+ @Override
+ public Context getContext() {
+ return mContext;
+ }
+
+ @Override
+ public Activity getActivity() {
+ return null;
+ }
+
+ @Override
+ public TabControl getTabControl() {
+ return null;
+ }
+
+ @Override
+ public WebViewFactory getWebViewFactory() {
+ return null;
+ }
+
+ @Override
+ public void onSetWebView(Tab tab, WebView view) {
+ }
+
+ @Override
+ public void createSubWindow(Tab tab) {
+ }
+
+ @Override
+ public void onPageStarted(Tab tab, WebView view, Bitmap favicon) {
+ }
+
+ @Override
+ public void onPageFinished(Tab tab) {
+ }
+
+ @Override
+ public void onProgressChanged(Tab tab) {
+ }
+
+ @Override
+ public void onReceivedTitle(Tab tab, String title) {
+ }
+
+ @Override
+ public void onFavicon(Tab tab, WebView view, Bitmap icon) {
+ }
+
+ @Override
+ public boolean shouldOverrideUrlLoading(Tab tab, WebView view, String url) {
+ return false;
+ }
+
+ @Override
+ public boolean shouldOverrideKeyEvent(KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public void onUnhandledKeyEvent(KeyEvent event) {
+ }
+
+ @Override
+ public void doUpdateVisitedHistory(Tab tab, boolean isReload) {
+ }
+
+ @Override
+ public void getVisitedHistory(ValueCallback<String[]> callback) {
+ }
+
+ @Override
+ public void onReceivedHttpAuthRequest(Tab tab, WebView view,
+ HttpAuthHandler handler, String host,
+ String realm) {
+ }
+
+ @Override
+ public void onDownloadStart(Tab tab, String url, String useragent,
+ String contentDisposition, String mimeType,
+ long contentLength) {
+ }
+
+ @Override
+ public void showCustomView(Tab tab, View view, int requestedOrientation,
+ CustomViewCallback callback) {
+ }
+
+ @Override
+ public void hideCustomView() {
+ }
+
+ @Override
+ public Bitmap getDefaultVideoPoster() {
+ return null;
+ }
+
+ @Override
+ public View getVideoLoadingProgressView() {
+ return null;
+ }
+
+ @Override
+ public void showSslCertificateOnError(WebView view,
+ SslErrorHandler handler, SslError error) {
+ }
+
+ @Override
+ public void onUserCanceledSsl(Tab tab) {
+ }
+
+ @Override
+ public void activateVoiceSearchMode(String title, List<String> results) {
+ }
+
+ @Override
+ public void revertVoiceSearchMode(Tab tab) {
+ }
+
+ @Override
+ public boolean shouldShowErrorConsole() {
+ return false;
+ }
+
+ @Override
+ public void onUpdatedLockIcon(Tab tab) {
+ }
+
+ @Override
+ public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
+ }
+
+ @Override
+ public void endActionMode() {
+ }
+
+ @Override
+ public void attachSubWindow(Tab tab) {
+ }
+
+ @Override
+ public void dismissSubWindow(Tab tab) {
+ }
+
+ @Override
+ public Tab openTab(String url, boolean incognito, boolean setActive,
+ boolean useCurrent) {
+ return null;
+ }
+
+ @Override
+ public Tab openTab(String url, Tab parent, boolean setActive,
+ boolean useCurrent) {
+ return null;
+ }
+
+ @Override
+ public boolean switchToTab(Tab tab) {
+ return false;
+ }
+
+ @Override
+ public void closeTab(Tab tab) {
+ }
+
+ @Override
+ public void setupAutoFill(Message message) {
+ }
+
+ @Override
+ public void bookmarkedStatusHasChanged(Tab tab) {
+ }
+
+ @Override
+ public void showAutoLogin(Tab tab) {
+ }
+
+ @Override
+ public void hideAutoLogin(Tab tab) {
+ }
+
+}
diff --git a/src/com/android/browser/PreloadRequestReceiver.java b/src/com/android/browser/PreloadRequestReceiver.java
new file mode 100644
index 00000000..c86d660b
--- /dev/null
+++ b/src/com/android/browser/PreloadRequestReceiver.java
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.browser;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.provider.Browser;
+import android.util.Log;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+
+/**
+ * Broadcast receiver for receiving browser preload requests
+ */
+public class PreloadRequestReceiver extends BroadcastReceiver {
+
+ private final static String LOGTAG = "browser.preloader";
+ private final static boolean LOGD_ENABLED = com.android.browser.Browser.LOGD_ENABLED;
+
+ private static final String ACTION_PRELOAD = "android.intent.action.PRELOAD";
+ static final String EXTRA_PRELOAD_ID = "preload_id";
+ static final String EXTRA_PRELOAD_DISCARD = "preload_discard";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (LOGD_ENABLED) Log.d(LOGTAG, "received intent " + intent);
+ if (BrowserSettings.getInstance().isPreloadEnabled()
+ && intent.getAction().equals(ACTION_PRELOAD)) {
+ handlePreload(context, intent);
+ }
+ }
+
+ private void handlePreload(Context context, Intent i) {
+ String url = UrlUtils.smartUrlFilter(i.getData());
+ String id = i.getStringExtra(EXTRA_PRELOAD_ID);
+ Map<String, String> headers = null;
+ if (id == null) {
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Preload request has no " + EXTRA_PRELOAD_ID);
+ return;
+ }
+ if (i.getBooleanExtra(EXTRA_PRELOAD_DISCARD, false)) {
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Got " + id + " preload discard request");
+ Preloader.getInstance().discardPreload(id);
+ } else {
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Got " + id + " preload request for " + url);
+ if (url != null && url.startsWith("http")) {
+ final Bundle pairs = i.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));
+ }
+ }
+ }
+ if (url != null) {
+ Preloader.getInstance().handlePreloadRequest(id, url, headers);
+ }
+ }
+ }
+
+}
diff --git a/src/com/android/browser/Preloader.java b/src/com/android/browser/Preloader.java
new file mode 100644
index 00000000..5a5f6873
--- /dev/null
+++ b/src/com/android/browser/Preloader.java
@@ -0,0 +1,137 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.browser;
+
+import android.content.Context;
+import android.os.Handler;
+import android.os.Looper;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.webkit.WebView;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Singleton class for handling preload requests.
+ */
+public class Preloader {
+
+ private final static String LOGTAG = "browser.preloader";
+ private final static boolean LOGD_ENABLED = true;//com.android.browser.Browser.LOGD_ENABLED;
+
+ private static final int PRERENDER_TIMEOUT_MILLIS = 30 * 1000; // 30s
+
+ private static Preloader sInstance;
+
+ private final Context mContext;
+ private final Handler mHandler;
+ private final BrowserWebViewFactory mFactory;
+ private final HashMap<String, PreloaderSession> mSessions;
+
+ public static void initialize(Context context) {
+ sInstance = new Preloader(context);
+ }
+
+ public static Preloader getInstance() {
+ return sInstance;
+ }
+
+ private Preloader(Context context) {
+ mContext = context;
+ mHandler = new Handler(Looper.getMainLooper());
+ mSessions = new HashMap<String, PreloaderSession>();
+ mFactory = new BrowserWebViewFactory(context);
+
+ }
+
+ private PreloaderSession getSession(String id) {
+ PreloaderSession s = mSessions.get(id);
+ if (s == null) {
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Create new preload session " + id);
+ s = new PreloaderSession(id);
+ mSessions.put(id, s);
+ }
+ return s;
+ }
+
+ private PreloaderSession takeSession(String id) {
+ PreloaderSession s = mSessions.remove(id);
+ if (s != null) {
+ s.cancelTimeout();
+ }
+ return s;
+ }
+
+ public void handlePreloadRequest(String id, String url, Map<String, String> headers) {
+ PreloaderSession s = getSession(id);
+ s.touch(); // reset timer
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Preloading " + url);
+ s.getTab().loadUrl(url, headers);
+ }
+
+ public void discardPreload(String id) {
+ PreloaderSession s = takeSession(id);
+ if (s != null) {
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Discard preload session " + id);
+ Tab t = s.getTab();
+ t.destroy();
+ }
+ }
+
+ /**
+ * Return a preloaded tab, and remove it from the preloader. This is used when the
+ * view is about to be displayed.
+ */
+ public Tab getPreloadedTab(String id) {
+ PreloaderSession s = takeSession(id);
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Showing preload session " + id + "=" + s);
+ return s == null ? null : s.getTab();
+ }
+
+ private class PreloaderSession {
+ private final String mId;
+ private final Tab mTab;
+
+ private final Runnable mTimeoutTask = new Runnable(){
+ @Override
+ public void run() {
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Preload session timeout " + mId);
+ discardPreload(mId);
+ }};
+
+ public PreloaderSession(String id) {
+ mId = id;
+ mTab = new Tab(new PreloadController(mContext), mFactory.createWebView(false));
+ touch();
+ }
+
+ public void cancelTimeout() {
+ mHandler.removeCallbacks(mTimeoutTask);
+ }
+
+ public void touch() {
+ cancelTimeout();
+ mHandler.postDelayed(mTimeoutTask, PRERENDER_TIMEOUT_MILLIS);
+ }
+
+ public Tab getTab() {
+ return mTab;
+ }
+
+ }
+
+}
diff --git a/src/com/android/browser/SnapshotTab.java b/src/com/android/browser/SnapshotTab.java
index 52a5c5f1..adccdf35 100644
--- a/src/com/android/browser/SnapshotTab.java
+++ b/src/com/android/browser/SnapshotTab.java
@@ -76,7 +76,7 @@ public class SnapshotTab extends Tab {
void loadData() {
if (mLoadTask == null) {
- mLoadTask = new LoadData(this, mActivity.getContentResolver());
+ mLoadTask = new LoadData(this, mContext.getContentResolver());
mLoadTask.execute();
}
}
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index e672e2bb..f8687a89 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -92,7 +92,7 @@ class Tab {
LOCK_ICON_MIXED,
}
- Activity mActivity;
+ Context mContext;
protected WebViewController mWebViewController;
// The tab ID
@@ -304,7 +304,7 @@ class Tab {
logIntent.putExtra(
LoggingEvents.VoiceSearch.EXTRA_N_BEST_CHOOSE_INDEX,
index);
- mActivity.sendBroadcast(logIntent);
+ mContext.sendBroadcast(logIntent);
}
if (mVoiceSearchData.mVoiceSearchIntent != null) {
// Copy the Intent, so that each history item will have its own
@@ -487,7 +487,7 @@ class Tab {
private void showError(ErrorDialog errDialog) {
if (mInForeground) {
- AlertDialog d = new AlertDialog.Builder(mActivity)
+ AlertDialog d = new AlertDialog.Builder(mContext)
.setTitle(errDialog.mTitle)
.setMessage(errDialog.mDescription)
.setPositiveButton(R.string.ok, null)
@@ -508,7 +508,7 @@ class Tab {
public void onPageStarted(WebView view, String url, Bitmap favicon) {
mInPageLoad = true;
mPageLoadProgress = 0;
- mCurrentState = new PageState(mActivity,
+ mCurrentState = new PageState(mContext,
view.isPrivateBrowsingEnabled(), url, favicon);
mLoadStartTime = SystemClock.uptimeMillis();
if (mVoiceSearchData != null
@@ -516,7 +516,7 @@ class Tab {
if (mVoiceSearchData.mSourceIsGoogle) {
Intent i = new Intent(LoggingEvents.ACTION_LOG_EVENT);
i.putExtra(LoggingEvents.EXTRA_FLUSH, true);
- mActivity.sendBroadcast(i);
+ mContext.sendBroadcast(i);
}
revertVoiceSearchMode();
}
@@ -587,7 +587,7 @@ class Tab {
Intent logIntent = new Intent(LoggingEvents.ACTION_LOG_EVENT);
logIntent.putExtra(LoggingEvents.EXTRA_EVENT,
LoggingEvents.VoiceSearch.RESULT_CLICKED);
- mActivity.sendBroadcast(logIntent);
+ mContext.sendBroadcast(logIntent);
}
if (mInForeground) {
return mWebViewController.shouldOverrideUrlLoading(Tab.this,
@@ -659,7 +659,7 @@ class Tab {
}
mDontResend = dontResend;
mResend = resend;
- new AlertDialog.Builder(mActivity).setTitle(
+ new AlertDialog.Builder(mContext).setTitle(
R.string.browserFrameFormResubmitLabel).setMessage(
R.string.browserFrameFormResubmitMessage)
.setPositiveButton(R.string.ok,
@@ -718,7 +718,7 @@ class Tab {
}
if (mSettings.showSecurityWarnings()) {
final LayoutInflater factory =
- LayoutInflater.from(mActivity);
+ LayoutInflater.from(mContext);
final View warningsView =
factory.inflate(R.layout.ssl_warnings, null);
final LinearLayout placeholder =
@@ -756,7 +756,7 @@ class Tab {
placeholder.addView(ll);
}
- new AlertDialog.Builder(mActivity).setTitle(
+ new AlertDialog.Builder(mContext).setTitle(
R.string.security_warning).setIcon(
android.R.drawable.ic_dialog_alert).setView(
warningsView).setPositiveButton(R.string.ssl_continue,
@@ -817,13 +817,14 @@ class Tab {
port = -1;
}
}
- KeyChain.choosePrivateKeyAlias(mActivity, new KeyChainAliasCallback() {
+ KeyChain.choosePrivateKeyAlias(
+ mWebViewController.getActivity(), new KeyChainAliasCallback() {
@Override public void alias(String alias) {
if (alias == null) {
handler.cancel();
return;
}
- new KeyChainLookup(mActivity, handler, alias).execute();
+ new KeyChainLookup(mContext, handler, alias).execute();
}
}, null, null, host, port, null);
}
@@ -846,7 +847,7 @@ class Tab {
public WebResourceResponse shouldInterceptRequest(WebView view,
String url) {
WebResourceResponse res = HomeProvider.shouldInterceptRequest(
- mActivity, url);
+ mContext, url);
return res;
}
@@ -869,7 +870,7 @@ class Tab {
@Override
public void onReceivedLoginRequest(WebView view, String realm,
String account, String args) {
- new DeviceAccountLogin(mActivity, view, Tab.this, mWebViewController)
+ new DeviceAccountLogin(mWebViewController.getActivity(), view, Tab.this, mWebViewController)
.handleLogin(realm, account, args);
}
@@ -916,7 +917,7 @@ class Tab {
}
// Short-circuit if we can't create any more tabs or sub windows.
if (dialog && mSubView != null) {
- new AlertDialog.Builder(mActivity)
+ new AlertDialog.Builder(mContext)
.setTitle(R.string.too_many_subwindows_dialog_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.too_many_subwindows_dialog_message)
@@ -924,7 +925,7 @@ class Tab {
.show();
return false;
} else if (!mWebViewController.getTabControl().canCreateNewTab()) {
- new AlertDialog.Builder(mActivity)
+ new AlertDialog.Builder(mContext)
.setTitle(R.string.too_many_windows_dialog_title)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.too_many_windows_dialog_message)
@@ -958,7 +959,7 @@ class Tab {
// Build a confirmation dialog to display to the user.
final AlertDialog d =
- new AlertDialog.Builder(mActivity)
+ new AlertDialog.Builder(mContext)
.setTitle(R.string.attention)
.setIcon(android.R.drawable.ic_dialog_alert)
.setMessage(R.string.popup_window_attempt)
@@ -1011,7 +1012,7 @@ class Tab {
@Override
public void onReceivedTouchIconUrl(WebView view, String url,
boolean precomposed) {
- final ContentResolver cr = mActivity.getContentResolver();
+ final ContentResolver cr = mContext.getContentResolver();
// Let precomposed icons take precedence over non-composed
// icons.
if (precomposed && mTouchIconLoader != null) {
@@ -1021,7 +1022,7 @@ class Tab {
// Have only one async task at a time.
if (mTouchIconLoader == null) {
mTouchIconLoader = new DownloadTouchIcon(Tab.this,
- mActivity, cr, view);
+ mContext, cr, view);
mTouchIconLoader.execute(url);
}
}
@@ -1029,7 +1030,10 @@ class Tab {
@Override
public void onShowCustomView(View view,
WebChromeClient.CustomViewCallback callback) {
- onShowCustomView(view, mActivity.getRequestedOrientation(), callback);
+ Activity activity = mWebViewController.getActivity();
+ if (activity != null) {
+ onShowCustomView(view, activity.getRequestedOrientation(), callback);
+ }
}
@Override
@@ -1202,8 +1206,8 @@ class Tab {
public void setupAutoFill(Message message) {
// Prompt the user to set up their profile.
final Message msg = message;
- AlertDialog.Builder builder = new AlertDialog.Builder(mActivity);
- LayoutInflater inflater = (LayoutInflater) mActivity.getSystemService(
+ AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
+ LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
Context.LAYOUT_INFLATER_SERVICE);
final View layout = inflater.inflate(R.layout.setup_autofill_dialog, null);
@@ -1217,7 +1221,7 @@ class Tab {
if (disableAutoFill.isChecked()) {
// Disable autofill and show a toast with how to turn it on again.
mSettings.setAutofillEnabled(false);
- Toast.makeText(mActivity,
+ Toast.makeText(mContext,
R.string.autofill_setup_dialog_negative_toast,
Toast.LENGTH_LONG).show();
} else {
@@ -1338,10 +1342,10 @@ class Tab {
// Construct a new tab
Tab(WebViewController wvcontroller, WebView w) {
mWebViewController = wvcontroller;
- mActivity = mWebViewController.getActivity();
+ mContext = mWebViewController.getContext();
mSettings = BrowserSettings.getInstance();
- mDataController = DataController.getInstance(mActivity);
- mCurrentState = new PageState(mActivity, w != null
+ mDataController = DataController.getInstance(mContext);
+ mCurrentState = new PageState(mContext, w != null
? w.isPrivateBrowsingEnabled() : false);
mInPageLoad = false;
mInForeground = false;
@@ -1373,6 +1377,10 @@ class Tab {
setWebView(w);
}
+ public void setController(WebViewController ctl) {
+ mWebViewController = ctl;
+ }
+
public void setId(long id) {
mId = id;
}
@@ -1468,7 +1476,7 @@ class Tab {
}
}
});
- mSubView.setOnCreateContextMenuListener(mActivity);
+ mSubView.setOnCreateContextMenuListener(mWebViewController.getActivity());
return true;
}
return false;
@@ -1558,9 +1566,10 @@ class Tab {
void putInForeground() {
mInForeground = true;
resume();
- mMainView.setOnCreateContextMenuListener(mActivity);
+ Activity activity = mWebViewController.getActivity();
+ mMainView.setOnCreateContextMenuListener(activity);
if (mSubView != null) {
- mSubView.setOnCreateContextMenuListener(mActivity);
+ mSubView.setOnCreateContextMenuListener(activity);
}
// Show the pending error dialog if the queue is not empty
if (mQueuedErrors != null && mQueuedErrors.size() > 0) {
@@ -1690,7 +1699,7 @@ class Tab {
*/
String getTitle() {
if (mCurrentState.mTitle == null && mInPageLoad) {
- return mActivity.getString(R.string.title_bar_loading);
+ return mContext.getString(R.string.title_bar_loading);
}
return mCurrentState.mTitle;
}
@@ -1716,7 +1725,7 @@ class Tab {
*/
ErrorConsoleView getErrorConsole(boolean createIfNecessary) {
if (createIfNecessary && mErrorConsole == null) {
- mErrorConsole = new ErrorConsoleView(mActivity);
+ mErrorConsole = new ErrorConsoleView(mContext);
mErrorConsole.setWebView(mMainView);
}
return mErrorConsole;
@@ -1882,7 +1891,7 @@ class Tab {
public void loadUrl(String url, Map<String, String> headers) {
if (mMainView != null) {
- mCurrentState = new PageState(mActivity, false, url, null);
+ mCurrentState = new PageState(mContext, false, url, null);
mWebViewController.onPageStarted(this, mMainView, null);
mMainView.loadUrl(url, headers);
}
diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java
index 2eb24e95..1110bda3 100644
--- a/src/com/android/browser/TabControl.java
+++ b/src/com/android/browser/TabControl.java
@@ -176,6 +176,14 @@ class TabControl {
return false;
}
+ void addPreloadedTab(Tab tab) {
+ tab.setId(getNextId());
+ mTabs.add(tab);
+ tab.setController(mController);
+ mController.onSetWebView(tab, tab.getWebView());
+ tab.putInBackground();
+ }
+
/**
* Create a new tab.
* @return The newly createTab or null if we have reached the maximum
diff --git a/src/com/android/browser/WebViewController.java b/src/com/android/browser/WebViewController.java
index 018af996..175cbf83 100644
--- a/src/com/android/browser/WebViewController.java
+++ b/src/com/android/browser/WebViewController.java
@@ -17,6 +17,7 @@
package com.android.browser;
import android.app.Activity;
+import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.net.http.SslError;
@@ -36,6 +37,8 @@ import java.util.List;
*/
public interface WebViewController {
+ Context getContext();
+
Activity getActivity();
TabControl getTabControl();
@@ -72,7 +75,7 @@ public interface WebViewController {
void onDownloadStart(Tab tab, String url, String useragent, String contentDisposition,
String mimeType, long contentLength);
- void showCustomView(Tab tab, View view, int requestedOrientation,
+ void showCustomView(Tab tab, View view, int requestedOrientation,
WebChromeClient.CustomViewCallback callback);
void hideCustomView();
diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java
index 8b43a748..8455e743 100644
--- a/src/com/android/browser/XLargeUi.java
+++ b/src/com/android/browser/XLargeUi.java
@@ -70,6 +70,14 @@ public class XLargeUi extends BaseUi implements ScrollListener {
}
@Override
+ public void onSetWebView(Tab tab, WebView v) {
+ super.onSetWebView(tab, v);
+ if (v != null) {
+ ((BrowserWebView) v).setScrollListener(this);
+ }
+ }
+
+ @Override
public void showComboView(boolean startWithHistory, Bundle extras) {
super.showComboView(startWithHistory, extras);
if (mUseQuickControls) {
@@ -135,21 +143,6 @@ public class XLargeUi extends BaseUi implements ScrollListener {
hideTitleBar();
}
- // webview factory
-
- @Override
- public WebView createWebView(boolean privateBrowsing) {
- // Create a new WebView
- BrowserWebView w = (BrowserWebView) super.createWebView(privateBrowsing);
- w.setScrollListener(this);
- return w;
- }
-
- @Override
- public WebView createSubWebView(boolean privateBrowsing) {
- return super.createWebView(privateBrowsing);
- }
-
@Override
public void onScroll(int visibleTitleHeight, boolean userInitiated) {
mTabBar.onScroll(visibleTitleHeight, userInitiated);
diff --git a/src/com/android/browser/preferences/BandwidthPreferencesFragment.java b/src/com/android/browser/preferences/BandwidthPreferencesFragment.java
new file mode 100644
index 00000000..18b9fa49
--- /dev/null
+++ b/src/com/android/browser/preferences/BandwidthPreferencesFragment.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.browser.preferences;
+
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.util.Log;
+
+import com.android.browser.PreferenceKeys;
+import com.android.browser.R;
+
+public class BandwidthPreferencesFragment extends PreferenceFragment {
+
+ static final String TAG = "BandwidthPreferencesFragment";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ // Load the XML preferences file
+ addPreferencesFromResource(R.xml.bandwidth_preferences);
+ }
+
+}