diff options
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/browser/Controller.java | 34 | ||||
-rw-r--r-- | src/com/android/browser/IntentHandler.java | 66 | ||||
-rw-r--r-- | src/com/android/browser/PreloadedTabControl.java | 32 | ||||
-rw-r--r-- | src/com/android/browser/Tab.java | 37 |
4 files changed, 158 insertions, 11 deletions
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java index 67c42dd..d05a845 100644 --- a/src/com/android/browser/Controller.java +++ b/src/com/android/browser/Controller.java @@ -18,6 +18,7 @@ package com.android.browser; 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.Calendar; 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 class Controller public void stopLoading() { mLoadStopped = true; Tab tab = mTabControl.getCurrentTab(); + tab.clearPageLoadCompleteListener(); WebView w = getCurrentTopWebView(); w.stopLoading(); mUi.onPageStopped(tab); @@ -2154,7 +2157,8 @@ public class Controller 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; @@ -2172,6 +2176,18 @@ public class Controller 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; } @@ -2365,10 +2381,26 @@ public class Controller // 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.Browser; 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 @@ public class IntentHandler { // "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 @@ public class IntentHandler { } } + /** + * 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 @@ public class IntentHandler { 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 @@ public class IntentHandler { } } } - return new UrlData(url, headers, intent, preloaded, preloadedSearchBoxQuery); + return new UrlData(url, headers, intent, preloaded, preloadedSearchBoxQuery, + loadCompletePendingIntent); } /** @@ -362,6 +414,7 @@ public class IntentHandler { final Intent mVoiceIntent; final PreloadedTabControl mPreloadedTab; final String mSearchBoxQueryToSubmit; + final PendingIntent mOnLoadCompletePendingIntent; UrlData(String url) { this.mUrl = url; @@ -369,14 +422,16 @@ public class IntentHandler { 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 @@ public class IntentHandler { } this.mPreloadedTab = preloaded; this.mSearchBoxQueryToSubmit = searchBoxQueryToSubmit; + this.mOnLoadCompletePendingIntent = onLoadCompletePendingIntent; } boolean isEmpty() { @@ -404,6 +460,10 @@ public class IntentHandler { String getSearchBoxQueryToSubmit() { return mSearchBoxQueryToSubmit; } + + PendingIntent getOnLoadCompletePendingIntent() { + return mOnLoadCompletePendingIntent; + } } } 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 @@ public class PreloadedTabControl { 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 @@ public class PreloadedTabControl { } } }); + 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 @@ public class PreloadedTabControl { 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 @@ public class PreloadedTabControl { 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; 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 @@ class Tab implements PictureListener { 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 @@ class Tab implements PictureListener { } } + 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 @@ class Tab implements PictureListener { @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 @@ class Tab implements PictureListener { mCurrentState = new PageState(mContext, false, url, null); mWebViewController.onPageStarted(this, mMainView, null); mMainView.loadUrl(url, headers); + clearPageLoadCompleteListener(); } } @@ -2093,12 +2122,14 @@ class Tab implements PictureListener { public void goBack() { if (mMainView != null) { + clearPageLoadCompleteListener(); mMainView.goBack(); } } public void goForward() { if (mMainView != null) { + clearPageLoadCompleteListener(); mMainView.goForward(); } } |