Merge "Fix omnibox ordering"
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 123e5c8..77e25a3 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -646,8 +646,25 @@
<string name="pref_data_title">Bandwidth Management</string>
<!-- Title for search preloading [CHAR LIMIT=40] -->
<string name="pref_data_preload_title">Search result preloading</string>
+ <!-- Search preloading options [CHAR LIMIT=20] -->
+ <string-array name="pref_data_preload_choices">
+ <item>Never</item>
+ <item>Only on Wi-Fi</item>
+ <item>Always</item>
+ </string-array>
+ <!-- Do not translate -->
+ <string name="pref_data_preload_value_never" translatable="false">NEVER</string>
+ <string name="pref_data_preload_value_wifi_only" translatable="false">WIFI_ONLY</string>
+ <string name="pref_data_preload_value_always" translatable="false">ALWAYS</string>
+ <string-array name="pref_data_preload_values" translatable="false">
+ <item><xliff:g>@string/pref_data_preload_value_never</xliff:g></item>
+ <item><xliff:g>@string/pref_data_preload_value_wifi_only</xliff:g></item>
+ <item><xliff:g>@string/pref_data_preload_value_always</xliff:g></item>
+ </string-array>
<!-- Summary for search preloading [CHAR LIMIT=80] -->
<string name="pref_data_preload_summary">Allow the browser to preload high confidence search results in the background</string>
+ <!-- Title of dialog for search preloading [CHAR LIMIT=40] -->
+ <string name="pref_data_preload_dialogtitle">Search result preloading</string>
<!-- Title for a dialog displayed when the browser has a data connectivity
problem -->
<string name="browserFrameNetworkErrorLabel">Data connectivity problem</string>
diff --git a/res/xml/bandwidth_preferences.xml b/res/xml/bandwidth_preferences.xml
index 0767ac5..8a31304 100644
--- a/res/xml/bandwidth_preferences.xml
+++ b/res/xml/bandwidth_preferences.xml
@@ -16,11 +16,14 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
- <CheckBoxPreference
- android:key="preload_enabled"
+ <ListPreference
+ android:key="preload_when"
android:title="@string/pref_data_preload_title"
android:summary="@string/pref_data_preload_summary"
- android:defaultValue="false" />
+ android:defaultValue="@string/pref_data_preload_value_wifi_only"
+ android:entries="@array/pref_data_preload_choices"
+ android:entryValues="@array/pref_data_preload_values"
+ android:dialogTitle="@string/pref_data_preload_dialogtitle" />
<CheckBoxPreference
android:key="load_images"
diff --git a/src/com/android/browser/BrowserSettings.java b/src/com/android/browser/BrowserSettings.java
index 11316ff..ce8a9c1 100644
--- a/src/com/android/browser/BrowserSettings.java
+++ b/src/com/android/browser/BrowserSettings.java
@@ -780,7 +780,20 @@
// getter/setters for bandwidth_preferences.xml
// -----------------------------
- public boolean isPreloadEnabled() {
- return mPrefs.getBoolean(PREF_DATA_PRELOAD, false);
+ public static String getPreloadOnWifiOnlyPreferenceString(Context context) {
+ return context.getResources().getString(R.string.pref_data_preload_value_wifi_only);
}
+
+ public static String getPreloadAlwaysPreferenceString(Context context) {
+ return context.getResources().getString(R.string.pref_data_preload_value_always);
+ }
+
+ private String getDefaultPreloadSetting() {
+ return getPreloadOnWifiOnlyPreferenceString(mContext);
+ }
+
+ public String getPreloadEnabled() {
+ return mPrefs.getString(PREF_DATA_PRELOAD, getDefaultPreloadSetting());
+ }
+
}
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index b7cdcd4..a3887f5 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -18,6 +18,7 @@
import android.app.Activity;
import android.app.DownloadManager;
+import android.app.PendingIntent;
import android.app.SearchManager;
import android.content.ClipboardManager;
import android.content.ContentProvider;
@@ -93,6 +94,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.regex.Pattern;
/**
* Controller for browser
@@ -761,6 +763,7 @@
public void stopLoading() {
mLoadStopped = true;
Tab tab = mTabControl.getCurrentTab();
+ tab.clearPageLoadCompleteListener();
WebView w = getCurrentTopWebView();
w.stopLoading();
mUi.onPageStopped(tab);
@@ -2160,7 +2163,8 @@
final PreloadedTabControl tabControl = urlData.getPreloadedTab();
final String sbQuery = urlData.getSearchBoxQueryToSubmit();
if (sbQuery != null) {
- if (!tabControl.searchBoxSubmit(sbQuery, urlData.mUrl, urlData.mHeaders)) {
+ if (!tabControl.searchBoxSubmit(sbQuery, urlData.mUrl, urlData.mHeaders,
+ urlData.getOnLoadCompletePendingIntent())) {
// Could not submit query. Fallback to regular tab creation
tabControl.destroy();
return null;
@@ -2178,6 +2182,18 @@
mTabControl.addPreloadedTab(t);
addTab(t);
setActiveTab(t);
+ if (sbQuery == null) {
+ // if the searchbox query is set, the load complete notification is handled within
+ // the preloaded tab controller.
+ if (t.inPageLoad()) {
+ requestLoadCompleteNotification(urlData.mOnLoadCompletePendingIntent, t,
+ urlData.mUrl, true, true);
+ } else {
+ // the page is already fully loaded
+ IntentHandler.sendPageLoadCompletePendingIntent(mActivity,
+ urlData.mOnLoadCompletePendingIntent, true, true);
+ }
+ }
return t;
}
@@ -2371,10 +2387,26 @@
// this isn't called for preloaded tabs
} else {
loadUrl(t, data.mUrl, data.mHeaders);
+ requestLoadCompleteNotification(data.mOnLoadCompletePendingIntent, t, data.mUrl,
+ null, null);
}
}
}
+ private void requestLoadCompleteNotification(final PendingIntent loadCompletePendingIntent,
+ Tab t, String forUrl, final Boolean preloaded, final Boolean preloadSuccess) {
+ if (loadCompletePendingIntent != null) {
+ Pattern urlMatch = Pattern.compile(Pattern.quote(forUrl));
+ t.setOnPageLoadCompleteListener(urlMatch, new Tab.OnPageLoadCompleteListener() {
+ @Override
+ public void onPageLoadComplete() {
+ IntentHandler.sendPageLoadCompletePendingIntent(mActivity,
+ loadCompletePendingIntent, preloaded, preloadSuccess);
+ }
+ });
+ }
+ }
+
@Override
public void onUserCanceledSsl(Tab tab) {
// TODO: Figure out the "right" behavior
diff --git a/src/com/android/browser/IntentHandler.java b/src/com/android/browser/IntentHandler.java
index cc6b57c..178ba62 100644
--- a/src/com/android/browser/IntentHandler.java
+++ b/src/com/android/browser/IntentHandler.java
@@ -18,6 +18,8 @@
package com.android.browser;
import android.app.Activity;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
import android.app.SearchManager;
import android.content.ContentResolver;
import android.content.Context;
@@ -30,6 +32,7 @@
import android.provider.MediaStore;
import android.speech.RecognizerResultsIntent;
import android.text.TextUtils;
+import android.util.Log;
import android.util.Patterns;
import com.android.browser.search.SearchEngine;
@@ -50,6 +53,22 @@
// "source" parameter for Google search from unknown source
final static String GOOGLE_SEARCH_SOURCE_UNKNOWN = "unknown";
+ // Pending intent extra attached to browser intents that is broadcast when the page load
+ // completes.
+ // TODO move to android.provider.Browser & make public?
+ private static final String EXTRA_LOAD_COMPLETE_PENDINGINTENT = "load_complete_intent";
+ // extra attached to intent received via EXTRA_LOAD_COMPLETE_PENDINGINTENT indicating the
+ // time at which the load completed.
+ public static final String EXTRA_LOAD_COMPLETION_TIME = "completets";
+ // extra attached to intent received via EXTRA_LOAD_COMPLETE_PENDINGINTENT indicating if
+ // preloading was attempted.
+ public static final String EXTRA_PREFETCH_ATTEMPTED = "prefattempt";
+ // extra attached to intent received via EXTRA_LOAD_COMPLETE_PENDINGINTENT indicating if
+ // preloading succeeded.
+ public static final String EXTRA_PREFETCH_SUCCESS = "prefsuccess";
+
+
+
/* package */ static final UrlData EMPTY_URL_DATA = new UrlData(null);
private Activity mActivity;
@@ -224,11 +243,39 @@
}
}
+ /**
+ * Send a pending intent received in a page view intent. This should be called when the page
+ * has finished loading.
+ *
+ * @param prefetchAttempted Indicates if prefetching was attempted, {@code null} if prefetching
+ * was not requested or is disabled.
+ * @param prefetchSucceeded Indicates if prefetching succeeded, {@code null} if prefetching
+ * was not requested or is disabled.
+ */
+ public static void sendPageLoadCompletePendingIntent(Context context, PendingIntent pi,
+ Boolean prefetchAttempted, Boolean prefetchSucceeded) {
+ if (pi == null) return;
+ Intent fillIn = new Intent();
+ fillIn.putExtra(EXTRA_LOAD_COMPLETION_TIME, System.currentTimeMillis());
+ if (prefetchAttempted != null) {
+ fillIn.putExtra(EXTRA_PREFETCH_ATTEMPTED, prefetchAttempted.booleanValue());
+ }
+ if (prefetchSucceeded != null) {
+ fillIn.putExtra(EXTRA_PREFETCH_SUCCESS, prefetchSucceeded.booleanValue());
+ }
+ try {
+ pi.send(context, Activity.RESULT_OK, fillIn);
+ } catch (CanceledException e) {
+ // ignore
+ }
+ }
+
protected static UrlData getUrlDataFromIntent(Intent intent) {
String url = "";
Map<String, String> headers = null;
PreloadedTabControl preloaded = null;
String preloadedSearchBoxQuery = null;
+ PendingIntent loadCompletePendingIntent = null;
if (intent != null
&& (intent.getFlags() & Intent.FLAG_ACTIVITY_LAUNCHED_FROM_HISTORY) == 0) {
final String action = intent.getAction();
@@ -253,6 +300,10 @@
PreloadRequestReceiver.EXTRA_SEARCHBOX_SETQUERY);
preloaded = Preloader.getInstance().getPreloadedTab(id);
}
+ if (intent.hasExtra(EXTRA_LOAD_COMPLETE_PENDINGINTENT)) {
+ loadCompletePendingIntent =
+ intent.getParcelableExtra(EXTRA_LOAD_COMPLETE_PENDINGINTENT);
+ }
} else if (Intent.ACTION_SEARCH.equals(action)
|| MediaStore.INTENT_ACTION_MEDIA_SEARCH.equals(action)
|| Intent.ACTION_WEB_SEARCH.equals(action)) {
@@ -277,7 +328,8 @@
}
}
}
- return new UrlData(url, headers, intent, preloaded, preloadedSearchBoxQuery);
+ return new UrlData(url, headers, intent, preloaded, preloadedSearchBoxQuery,
+ loadCompletePendingIntent);
}
/**
@@ -362,6 +414,7 @@
final Intent mVoiceIntent;
final PreloadedTabControl mPreloadedTab;
final String mSearchBoxQueryToSubmit;
+ final PendingIntent mOnLoadCompletePendingIntent;
UrlData(String url) {
this.mUrl = url;
@@ -369,14 +422,16 @@
this.mVoiceIntent = null;
this.mPreloadedTab = null;
this.mSearchBoxQueryToSubmit = null;
+ this.mOnLoadCompletePendingIntent = null;
}
UrlData(String url, Map<String, String> headers, Intent intent) {
- this(url, headers, intent, null, null);
+ this(url, headers, intent, null, null, null);
}
UrlData(String url, Map<String, String> headers, Intent intent,
- PreloadedTabControl preloaded, String searchBoxQueryToSubmit) {
+ PreloadedTabControl preloaded, String searchBoxQueryToSubmit,
+ PendingIntent onLoadCompletePendingIntent) {
this.mUrl = url;
this.mHeaders = headers;
if (RecognizerResultsIntent.ACTION_VOICE_SEARCH_RESULTS
@@ -387,6 +442,7 @@
}
this.mPreloadedTab = preloaded;
this.mSearchBoxQueryToSubmit = searchBoxQueryToSubmit;
+ this.mOnLoadCompletePendingIntent = onLoadCompletePendingIntent;
}
boolean isEmpty() {
@@ -404,6 +460,10 @@
String getSearchBoxQueryToSubmit() {
return mSearchBoxQueryToSubmit;
}
+
+ PendingIntent getOnLoadCompletePendingIntent() {
+ return mOnLoadCompletePendingIntent;
+ }
}
}
diff --git a/src/com/android/browser/PreferenceKeys.java b/src/com/android/browser/PreferenceKeys.java
index 1bfe389..f3da937 100644
--- a/src/com/android/browser/PreferenceKeys.java
+++ b/src/com/android/browser/PreferenceKeys.java
@@ -100,7 +100,7 @@
// ----------------------
// Keys for bandwidth_preferences.xml
// ----------------------
- static final String PREF_DATA_PRELOAD = "preload_enabled";
+ static final String PREF_DATA_PRELOAD = "preload_when";
static final String PREF_LOAD_IMAGES = "load_images";
}
diff --git a/src/com/android/browser/PreloadRequestReceiver.java b/src/com/android/browser/PreloadRequestReceiver.java
index ff3c8e3..c654037 100644
--- a/src/com/android/browser/PreloadRequestReceiver.java
+++ b/src/com/android/browser/PreloadRequestReceiver.java
@@ -18,9 +18,9 @@
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
-import android.content.SharedPreferences;
+import android.net.ConnectivityManager;
+import android.net.NetworkInfo;
import android.os.Bundle;
-import android.preference.PreferenceManager;
import android.provider.Browser;
import android.util.Log;
@@ -42,15 +42,57 @@
static final String EXTRA_SEARCHBOX_CANCEL = "searchbox_cancel";
static final String EXTRA_SEARCHBOX_SETQUERY = "searchbox_query";
+ private ConnectivityManager mConnectivityManager;
+
@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)) {
+ if (isPreloadEnabledOnCurrentNetwork(context) &&
+ intent.getAction().equals(ACTION_PRELOAD)) {
handlePreload(context, intent);
}
}
+ private boolean isPreloadEnabledOnCurrentNetwork(Context context) {
+ String preload = BrowserSettings.getInstance().getPreloadEnabled();
+ if (LOGD_ENABLED) Log.d(LOGTAG, "Preload setting: " + preload);
+ if (BrowserSettings.getPreloadAlwaysPreferenceString(context).equals(preload)) {
+ return true;
+ } else if (BrowserSettings.getPreloadOnWifiOnlyPreferenceString(context).equals(preload)) {
+ boolean onWifi = isOnWifi(context);
+ if (LOGD_ENABLED) Log.d(LOGTAG, "on wifi:" + onWifi);
+ return onWifi;
+ } else {
+ return false;
+ }
+ }
+
+ private boolean isOnWifi(Context context) {
+ if (mConnectivityManager == null) {
+ mConnectivityManager = (ConnectivityManager)
+ context.getSystemService(Context.CONNECTIVITY_SERVICE);
+ }
+ NetworkInfo ni = mConnectivityManager.getActiveNetworkInfo();
+ if (ni == null) {
+ return false;
+ }
+ switch (ni.getType()) {
+ case ConnectivityManager.TYPE_MOBILE:
+ case ConnectivityManager.TYPE_MOBILE_DUN:
+ case ConnectivityManager.TYPE_MOBILE_MMS:
+ case ConnectivityManager.TYPE_MOBILE_SUPL:
+ case ConnectivityManager.TYPE_MOBILE_HIPRI:
+ case ConnectivityManager.TYPE_WIMAX: // separate case for this?
+ return false;
+ case ConnectivityManager.TYPE_WIFI:
+ case ConnectivityManager.TYPE_ETHERNET:
+ case ConnectivityManager.TYPE_BLUETOOTH:
+ return true;
+ default:
+ return false;
+ }
+ }
+
private void handlePreload(Context context, Intent i) {
String url = UrlUtils.smartUrlFilter(i.getData());
String id = i.getStringExtra(EXTRA_PRELOAD_ID);
diff --git a/src/com/android/browser/PreloadedTabControl.java b/src/com/android/browser/PreloadedTabControl.java
index 4ffe6b4..b0eff63 100644
--- a/src/com/android/browser/PreloadedTabControl.java
+++ b/src/com/android/browser/PreloadedTabControl.java
@@ -15,6 +15,7 @@
*/
package com.android.browser;
+import android.app.PendingIntent;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;
@@ -39,7 +40,7 @@
mTab = t;
}
- private void maybeSetQuery(final String query, SearchBox sb) {
+ private boolean maybeSetQuery(final String query, SearchBox sb) {
if (!TextUtils.equals(mLastQuery, query)) {
if (sb != null) {
if (LOGD_ENABLED) Log.d(LOGTAG, "Changing searchbox query to " + query);
@@ -55,25 +56,27 @@
}
}
});
+ return true;
} else {
if (LOGD_ENABLED) Log.d(LOGTAG, "Cannot set query: no searchbox interface");
}
}
+ return false;
}
public void setQuery(String query) {
maybeSetQuery(query, mTab.getWebView().getSearchBox());
}
- public boolean searchBoxSubmit(final String query,
- final String fallbackUrl, final Map<String, String> fallbackHeaders) {
+ public boolean searchBoxSubmit(final String query, final String fallbackUrl,
+ final Map<String, String> fallbackHeaders, final PendingIntent onLoadCompleteIntent) {
final SearchBox sb = mTab.getWebView().getSearchBox();
if (sb == null) {
// no searchbox, cannot submit. Fallback to regular tab creation
if (LOGD_ENABLED) Log.d(LOGTAG, "No searchbox, cannot submit query");
return false;
}
- maybeSetQuery(query, sb);
+ final boolean newQuery = maybeSetQuery(query, sb);
if (LOGD_ENABLED) Log.d(LOGTAG, "Submitting query " + query);
final String currentUrl = mTab.getUrl();
sb.onsubmit(new SearchBox.SearchBoxListener() {
@@ -84,9 +87,14 @@
if (!called) {
if (LOGD_ENABLED) Log.d(LOGTAG, "Query not submitted; falling back");
loadUrl(fallbackUrl, fallbackHeaders);
+
// make sure that the failed, preloaded URL is cleared from the back stack
mTab.clearBackStackWhenItemAdded(Pattern.compile(
"^" + Pattern.quote(fallbackUrl) + "$"));
+ // When setting the search box query, preloadAttempted=true implies that the
+ // the query was prefetched using the searchbox API. This is the case if we
+ // the query is not new.
+ registerLoadCompleteListener(!newQuery, false, onLoadCompleteIntent);
} else {
// ignore the next fragment change, to avoid leaving a blank page in the browser
// after the query has been submitted.
@@ -100,11 +108,27 @@
Pattern.quote(currentWithoutFragment) +
"(\\#.*)?" +
"$"));
+ registerLoadCompleteListener(!newQuery, true, onLoadCompleteIntent);
}
}});
return true;
}
+ private void registerLoadCompleteListener(
+ final boolean queryPreloaded,
+ final boolean preloadSucceeded,
+ final PendingIntent pendingIntent) {
+ if (pendingIntent == null) {
+ return;
+ }
+ mTab.setOnPageLoadCompleteListener(null, new Tab.OnPageLoadCompleteListener(){
+ @Override
+ public void onPageLoadComplete() {
+ IntentHandler.sendPageLoadCompletePendingIntent(mTab.mContext, pendingIntent,
+ queryPreloaded, preloadSucceeded);
+ }});
+ }
+
public void searchBoxCancel() {
SearchBox sb = mTab.getWebView().getSearchBox();
if (sb != null) {
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 8c9dc02..8dcc54b 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -66,8 +66,6 @@
import android.webkit.WebView.PictureListener;
import android.webkit.WebViewClient;
import android.widget.CheckBox;
-import android.widget.LinearLayout;
-import android.widget.TextView;
import android.widget.Toast;
import com.android.browser.TabControl.OnThumbnailUpdatedListener;
@@ -174,10 +172,13 @@
private Handler mHandler;
/**
- * See {@link #clearBackStackWhenItemAdded(String)}.
+ * See {@link #clearBackStackWhenItemAdded(Pattern)}.
*/
private Pattern mClearHistoryUrlPattern;
+ private OnPageLoadCompleteListener mOnPageLoadCompleteListener;
+ private Pattern mOnPageLoadCompleteUrlMatch;
+
private static synchronized Bitmap getDefaultFavicon(Context context) {
if (sDefaultFavicon == null) {
sDefaultFavicon = BitmapFactory.decodeResource(
@@ -532,6 +533,26 @@
}
}
+ public interface OnPageLoadCompleteListener {
+ void onPageLoadComplete();
+ }
+
+ /**
+ * Requests a notification when the next page load completes. This is a one shot notification,
+ * the listener will be discarded after the first callback, or if the page load is cancelled.
+ * @param listener
+ */
+ public void setOnPageLoadCompleteListener(Pattern urlMatch,
+ OnPageLoadCompleteListener listener) {
+ mOnPageLoadCompleteListener = listener;
+ mOnPageLoadCompleteUrlMatch = urlMatch;
+ }
+
+ public void clearPageLoadCompleteListener() {
+ mOnPageLoadCompleteListener = null;
+ mOnPageLoadCompleteUrlMatch = null;
+ }
+
// -------------------------------------------------------------------------
// WebViewClient implementation for the main WebView
// -------------------------------------------------------------------------
@@ -595,6 +616,13 @@
@Override
public void onPageFinished(WebView view, String url) {
+ if (mOnPageLoadCompleteListener != null) {
+ if (mOnPageLoadCompleteUrlMatch == null
+ || mOnPageLoadCompleteUrlMatch.matcher(url).matches())
+ mOnPageLoadCompleteListener.onPageLoadComplete();
+ mOnPageLoadCompleteListener = null;
+ mOnPageLoadCompleteUrlMatch = null;
+ }
if (!mInPageLoad) {
// In page navigation links (www.something.com#footer) will
// trigger an onPageFinished which we don't care about.
@@ -2042,6 +2070,7 @@
mCurrentState = new PageState(mContext, false, url, null);
mWebViewController.onPageStarted(this, mMainView, null);
mMainView.loadUrl(url, headers);
+ clearPageLoadCompleteListener();
}
}
@@ -2093,12 +2122,14 @@
public void goBack() {
if (mMainView != null) {
+ clearPageLoadCompleteListener();
mMainView.goBack();
}
}
public void goForward() {
if (mMainView != null) {
+ clearPageLoadCompleteListener();
mMainView.goForward();
}
}