diff options
author | John Reck <jreck@google.com> | 2011-07-26 10:22:22 -0700 |
---|---|---|
committer | John Reck <jreck@google.com> | 2011-07-27 14:11:44 -0700 |
commit | 1cf4b79a0020bc18c83ca8bde0e318ecd5252bc2 (patch) | |
tree | ee1b1ce7a7c86f9a6226e62badf61e6e1bed3b4c /src | |
parent | ff6a748ae78165cc2298f5120a4374b4ed6f836b (diff) | |
download | packages_apps_browser-1cf4b79a0020bc18c83ca8bde0e318ecd5252bc2.zip packages_apps_browser-1cf4b79a0020bc18c83ca8bde0e318ecd5252bc2.tar.gz packages_apps_browser-1cf4b79a0020bc18c83ca8bde0e318ecd5252bc2.tar.bz2 |
Restore overhaul
Bug: 5069192
Store thumbnails in a database restored async for each tab
Fix restoring a tab not restoring its current state
Change-Id: I2c14e352638aac0ef766fb3bf4036ff220c53ecd
Diffstat (limited to 'src')
-rw-r--r-- | src/com/android/browser/BaseUi.java | 14 | ||||
-rw-r--r-- | src/com/android/browser/BrowserActivity.java | 4 | ||||
-rw-r--r-- | src/com/android/browser/Controller.java | 59 | ||||
-rw-r--r-- | src/com/android/browser/CrashRecoveryHandler.java | 2 | ||||
-rw-r--r-- | src/com/android/browser/PhoneUi.java | 5 | ||||
-rw-r--r-- | src/com/android/browser/PieControlPhone.java | 2 | ||||
-rw-r--r-- | src/com/android/browser/PieControlXLarge.java | 2 | ||||
-rw-r--r-- | src/com/android/browser/PreloadController.java | 5 | ||||
-rw-r--r-- | src/com/android/browser/SnapshotTab.java | 18 | ||||
-rw-r--r-- | src/com/android/browser/Tab.java | 273 | ||||
-rw-r--r-- | src/com/android/browser/TabControl.java | 43 | ||||
-rw-r--r-- | src/com/android/browser/UI.java | 2 | ||||
-rw-r--r-- | src/com/android/browser/WebViewController.java | 2 | ||||
-rw-r--r-- | src/com/android/browser/XLargeUi.java | 9 | ||||
-rw-r--r-- | src/com/android/browser/provider/BrowserProvider2.java | 60 |
15 files changed, 359 insertions, 141 deletions
diff --git a/src/com/android/browser/BaseUi.java b/src/com/android/browser/BaseUi.java index b270dea..091126c 100644 --- a/src/com/android/browser/BaseUi.java +++ b/src/com/android/browser/BaseUi.java @@ -738,20 +738,6 @@ public abstract class BaseUi implements UI, OnTouchListener { warning.show(); } - protected void captureTab(final Tab tab) { - captureTab(tab, - (int) mActivity.getResources() - .getDimension(R.dimen.qc_thumb_width), - (int) mActivity.getResources() - .getDimension(R.dimen.qc_thumb_height)); - } - - protected void captureTab(final Tab tab, int width, int height) { - if ((tab == null) || (tab.getWebView() == null)) return; - Bitmap sshot = Controller.createScreenshot(tab, width, height); - tab.setScreenshot(sshot); - } - protected WebView getWebView() { if (mActiveTab != null) { return mActiveTab.getWebView(); diff --git a/src/com/android/browser/BrowserActivity.java b/src/com/android/browser/BrowserActivity.java index 0df2e94..6ec6071 100644 --- a/src/com/android/browser/BrowserActivity.java +++ b/src/com/android/browser/BrowserActivity.java @@ -112,7 +112,7 @@ public class BrowserActivity extends Activity { protected void onNewIntent(Intent intent) { if (ACTION_RESTART.equals(intent.getAction())) { Bundle outState = new Bundle(); - mController.onSaveInstanceState(outState, true); + mController.onSaveInstanceState(outState); finish(); getApplicationContext().startActivity( new Intent(getApplicationContext(), BrowserActivity.class) @@ -163,7 +163,7 @@ public class BrowserActivity extends Activity { if (LOGV_ENABLED) { Log.v(LOGTAG, "BrowserActivity.onSaveInstanceState: this=" + this); } - mController.onSaveInstanceState(outState, true); + mController.onSaveInstanceState(outState); } @Override diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java index b3be618..92f448c 100644 --- a/src/com/android/browser/Controller.java +++ b/src/com/android/browser/Controller.java @@ -80,6 +80,7 @@ import com.android.browser.IntentHandler.UrlData; import com.android.browser.UI.ComboViews; import com.android.browser.UI.DropdownChangeListener; import com.android.browser.provider.BrowserProvider; +import com.android.browser.provider.BrowserProvider2.Thumbnails; import com.android.browser.provider.SnapshotProvider.Snapshots; import com.android.browser.search.SearchEngine; import com.android.common.Search; @@ -87,6 +88,7 @@ import com.android.common.Search; import java.io.ByteArrayOutputStream; import java.io.File; import java.net.URLEncoder; +import java.util.ArrayList; import java.util.Calendar; import java.util.HashMap; import java.util.List; @@ -309,6 +311,7 @@ public class Controller private void onPreloginFinished(Bundle icicle, Intent intent, long currentTabId, boolean restoreIncognitoTabs) { if (currentTabId == -1) { + BackgroundHandler.execute(new PruneThumbnails(mActivity, null)); final Bundle extra = intent.getExtras(); // Create an initial tab. // If the intent is ACTION_VIEW and data is not null, the Browser is @@ -335,7 +338,13 @@ public class Controller } else { mTabControl.restoreState(icicle, currentTabId, restoreIncognitoTabs, mUi.needsRestoreAllTabs()); - mUi.updateTabs(mTabControl.getTabs()); + List<Tab> tabs = mTabControl.getTabs(); + ArrayList<Long> restoredTabs = new ArrayList<Long>(tabs.size()); + for (Tab t : tabs) { + restoredTabs.add(t.getId()); + } + BackgroundHandler.execute(new PruneThumbnails(mActivity, restoredTabs)); + mUi.updateTabs(tabs); // TabControl.restoreState() will create a new tab even if // restoring the state fails. setActiveTab(mTabControl.getCurrentTab()); @@ -357,6 +366,38 @@ public class Controller } } + private static class PruneThumbnails implements Runnable { + private Context mContext; + private List<Long> mIds; + + PruneThumbnails(Context context, List<Long> preserveIds) { + mContext = context.getApplicationContext(); + mIds = preserveIds; + } + + @Override + public void run() { + ContentResolver cr = mContext.getContentResolver(); + if (mIds == null || mIds.size() == 0) { + cr.delete(Thumbnails.CONTENT_URI, null, null); + } else { + int length = mIds.size(); + StringBuilder where = new StringBuilder(); + where.append(Thumbnails._ID); + where.append(" not in ("); + for (int i = 0; i < length; i++) { + where.append(mIds.get(i)); + if (i < (length - 1)) { + where.append(","); + } + } + where.append(")"); + cr.delete(Thumbnails.CONTENT_URI, where.toString(), null); + } + } + + } + @Override public WebViewFactory getWebViewFactory() { return mFactory; @@ -612,7 +653,7 @@ public class Controller } - void onSaveInstanceState(Bundle outState, boolean saveImages) { + void onSaveInstanceState(Bundle outState) { // the default implementation requires each view to have an id. As the // browser handles the state itself and it doesn't use id for the views, // don't call the default implementation. Otherwise it will trigger the @@ -620,7 +661,7 @@ public class Controller // focused view XXX has no id". // Save all the tabs - mTabControl.saveState(outState, false); + mTabControl.saveState(outState); if (!outState.isEmpty()) { // Save time so that we know how old incognito tabs (if any) are. outState.putSerializable("lastActiveDate", Calendar.getInstance()); @@ -1902,13 +1943,6 @@ public class Controller R.dimen.bookmarkThumbnailHeight); } - static Bitmap createScreenshot(Tab tab, int width, int height) { - if ((tab != null) && (tab.getWebView() != null)) { - return createScreenshot(tab.getWebView(), width, height); - } - return null; - } - static Bitmap createScreenshot(WebView view, int width, int height) { // We render to a bitmap 2x the desired size so that we can then // re-scale it with filtering since canvas.scale doesn't filter @@ -2646,4 +2680,9 @@ public class Controller return true; } + @Override + public boolean shouldCaptureThumbnails() { + return mUi.shouldCaptureThumbnails(); + } + } diff --git a/src/com/android/browser/CrashRecoveryHandler.java b/src/com/android/browser/CrashRecoveryHandler.java index 02636c0..fdcdbc6 100644 --- a/src/com/android/browser/CrashRecoveryHandler.java +++ b/src/com/android/browser/CrashRecoveryHandler.java @@ -133,7 +133,7 @@ public class CrashRecoveryHandler { public void run() { try { final Bundle state = new Bundle(); - mController.onSaveInstanceState(state, false); + mController.onSaveInstanceState(state); Message.obtain(mBackgroundHandler, MSG_WRITE_STATE, state) .sendToTarget(); // Remove any queued up saves diff --git a/src/com/android/browser/PhoneUi.java b/src/com/android/browser/PhoneUi.java index 28db6f0..1c9d5a0 100644 --- a/src/com/android/browser/PhoneUi.java +++ b/src/com/android/browser/PhoneUi.java @@ -276,4 +276,9 @@ public class PhoneUi extends BaseUi { } } + @Override + public boolean shouldCaptureThumbnails() { + return true; + } + } diff --git a/src/com/android/browser/PieControlPhone.java b/src/com/android/browser/PieControlPhone.java index c4b28fa..0b62cef 100644 --- a/src/com/android/browser/PieControlPhone.java +++ b/src/com/android/browser/PieControlPhone.java @@ -72,7 +72,7 @@ public class PieControlPhone extends PieControlBase implements OnClickListener { private void buildTabs() { final List<Tab> tabs = mUiController.getTabs(); - mUi.captureTab(mUi.getActiveTab()); + mUi.getActiveTab().capture(); mTabAdapter.setTabs(tabs); PieStackView sym = (PieStackView) mShowTabs.getPieView(); sym.setCurrent(mUiController.getTabControl().getCurrentPosition()); diff --git a/src/com/android/browser/PieControlXLarge.java b/src/com/android/browser/PieControlXLarge.java index a036e0d..95f586e 100644 --- a/src/com/android/browser/PieControlXLarge.java +++ b/src/com/android/browser/PieControlXLarge.java @@ -112,7 +112,7 @@ public class PieControlXLarge extends PieControlBase implements OnClickListener private void buildTabs() { final List<Tab> tabs = mUiController.getTabs(); - mUi.captureTab(mUi.getActiveTab()); + mUi.getActiveTab().capture(); mTabAdapter.setTabs(tabs); PieStackView sym = (PieStackView) mShowTabs.getPieView(); sym.setCurrent(mUiController.getTabControl().getCurrentPosition()); diff --git a/src/com/android/browser/PreloadController.java b/src/com/android/browser/PreloadController.java index 652ea8e..08e223f 100644 --- a/src/com/android/browser/PreloadController.java +++ b/src/com/android/browser/PreloadController.java @@ -276,4 +276,9 @@ public class PreloadController implements WebViewController { if (LOGD_ENABLED) Log.d(LOGTAG, "hideAutoLogin()"); } + @Override + public boolean shouldCaptureThumbnails() { + return false; + } + } diff --git a/src/com/android/browser/SnapshotTab.java b/src/com/android/browser/SnapshotTab.java index e57502f..bd6dd5b 100644 --- a/src/com/android/browser/SnapshotTab.java +++ b/src/com/android/browser/SnapshotTab.java @@ -22,6 +22,7 @@ import android.database.Cursor; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.AsyncTask; +import android.os.Bundle; import android.util.Log; import android.webkit.WebView; @@ -44,7 +45,7 @@ public class SnapshotTab extends Tab { private boolean mIsLive; public SnapshotTab(WebViewController wvcontroller, long snapshotId) { - super(wvcontroller, null); + super(wvcontroller, null, null); mSnapshotId = snapshotId; mWebViewFactory = mWebViewController.getWebViewFactory(); WebView web = mWebViewFactory.createWebView(false); @@ -98,8 +99,8 @@ public class SnapshotTab extends Tab { } @Override - boolean saveState() { - return false; + Bundle saveState() { + return null; } public long getDateCreated() { @@ -198,4 +199,15 @@ public class SnapshotTab extends Tab { } } + + @Override + protected void persistThumbnail() { + // Nope + } + + @Override + protected void deleteThumbnail() { + // Nope + } + } diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java index beac2ff..a4bcc99 100644 --- a/src/com/android/browser/Tab.java +++ b/src/com/android/browser/Tab.java @@ -20,11 +20,13 @@ import android.app.Activity; import android.app.AlertDialog; import android.app.SearchManager; import android.content.ContentResolver; +import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.DialogInterface.OnCancelListener; import android.content.Intent; +import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Bitmap.CompressFormat; import android.graphics.BitmapFactory; @@ -53,7 +55,6 @@ import android.webkit.HttpAuthHandler; import android.webkit.SslErrorHandler; import android.webkit.URLUtil; import android.webkit.ValueCallback; -import android.webkit.WebBackForwardList; import android.webkit.WebBackForwardListClient; import android.webkit.WebChromeClient; import android.webkit.WebHistoryItem; @@ -68,10 +69,12 @@ import android.widget.TextView; import android.widget.Toast; import com.android.browser.homepages.HomeProvider; +import com.android.browser.provider.BrowserProvider2.Thumbnails; import com.android.browser.provider.SnapshotProvider.Snapshots; import com.android.common.speech.LoggingEvents; import java.io.ByteArrayOutputStream; +import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; @@ -95,6 +98,8 @@ class Tab implements PictureListener { private static final int MSG_CAPTURE = 42; private static final int CAPTURE_DELAY = 500; + private static Bitmap sDefaultFavicon; + public enum LockIcon { LOCK_ICON_UNSECURE, LOCK_ICON_SECURE, @@ -161,6 +166,13 @@ class Tab implements PictureListener { private Bitmap mCapture; private Handler mHandler; + private static synchronized Bitmap getDefaultFavicon(Context context) { + if (sDefaultFavicon == null) { + sDefaultFavicon = BitmapFactory.decodeResource( + context.getResources(), R.drawable.app_web_browser_sm); + } + return sDefaultFavicon; + } // All the state needed for a page protected static class PageState { @@ -179,8 +191,7 @@ class Tab implements PictureListener { mOriginalUrl = mUrl = ""; mTitle = c.getString(R.string.new_tab); } - mFavicon = BitmapFactory.decodeResource( - c.getResources(), R.drawable.app_web_browser_sm); + mFavicon = null; mLockIcon = LockIcon.LOCK_ICON_UNSECURE; } @@ -192,13 +203,9 @@ class Tab implements PictureListener { } else { mLockIcon = LockIcon.LOCK_ICON_UNSECURE; } - if (favicon != null) { - mFavicon = favicon; - } else { - mFavicon = BitmapFactory.decodeResource( - c.getResources(), R.drawable.app_web_browser_sm); - } + mFavicon = favicon; } + } // The current/loading page's state @@ -211,7 +218,6 @@ class Tab implements PictureListener { static final String PARENTTAB = "parentTab"; static final String APPID = "appid"; static final String INCOGNITO = "privateBrowsingEnabled"; - static final String SCREENSHOT = "screenshot"; static final String USERAGENT = "useragent"; // ------------------------------------------------------------------------- @@ -576,19 +582,7 @@ class Tab implements PictureListener { url, SystemClock.uptimeMillis() - mLoadStartTime); } mInPageLoad = false; - // Sync state (in case of stop/timeout) - mCurrentState.mUrl = view.getUrl(); - if (mCurrentState.mUrl == null) { - mCurrentState.mUrl = url != null ? url : ""; - } - mCurrentState.mOriginalUrl = view.getOriginalUrl(); - mCurrentState.mTitle = view.getTitle(); - mCurrentState.mFavicon = view.getFavicon(); - if (!URLUtil.isHttpsUrl(mCurrentState.mUrl)) { - // In case we stop when loading an HTTPS page from an HTTP page - // but before a provisional load occurred - mCurrentState.mLockIcon = LockIcon.LOCK_ICON_UNSECURE; - } + syncCurrentState(view, url); mWebViewController.onPageFinished(Tab.this); } @@ -894,6 +888,22 @@ class Tab implements PictureListener { }; + private void syncCurrentState(WebView view, String url) { + // Sync state (in case of stop/timeout) + mCurrentState.mUrl = view.getUrl(); + if (mCurrentState.mUrl == null) { + mCurrentState.mUrl = url != null ? url : ""; + } + mCurrentState.mOriginalUrl = view.getOriginalUrl(); + mCurrentState.mTitle = view.getTitle(); + mCurrentState.mFavicon = view.getFavicon(); + if (!URLUtil.isHttpsUrl(mCurrentState.mUrl)) { + // In case we stop when loading an HTTPS page from an HTTP page + // but before a provisional load occurred + mCurrentState.mLockIcon = LockIcon.LOCK_ICON_UNSECURE; + } + } + // Called by DeviceAccountLogin when the Tab needs to have the auto-login UI // displayed. void setDeviceAccountLogin(DeviceAccountLogin login) { @@ -1355,11 +1365,16 @@ class Tab implements PictureListener { // ------------------------------------------------------------------------- - // TODO temporarily use activity here - // remove later - // Construct a new tab Tab(WebViewController wvcontroller, WebView w) { + this(wvcontroller, w, null); + } + + Tab(WebViewController wvcontroller, Bundle state) { + this(wvcontroller, null, state); + } + + Tab(WebViewController wvcontroller, WebView w, Bundle state) { mWebViewController = wvcontroller; mContext = mWebViewController.getContext(); mSettings = BrowserSettings.getInstance(); @@ -1393,21 +1408,46 @@ class Tab implements PictureListener { } }; + mCaptureWidth = mContext.getResources().getDimensionPixelSize( + R.dimen.tab_thumbnail_width); + mCaptureHeight = mContext.getResources().getDimensionPixelSize( + R.dimen.tab_thumbnail_height); + updateShouldCaptureThumbnails(); + restoreState(state); setWebView(w); - mCaptureWidth = mContext.getResources().getDimensionPixelSize(R.dimen.nav_tab_width); - mCaptureHeight = mContext.getResources().getDimensionPixelSize(R.dimen.nav_tab_height); - mCapture = Bitmap.createBitmap(mCaptureWidth, mCaptureHeight, - Bitmap.Config.RGB_565); mHandler = new Handler() { public void handleMessage(Message m) { - Tab.this.capture(); + switch (m.what) { + case MSG_CAPTURE: + capture(); + break; + } } }; + } + public void updateShouldCaptureThumbnails() { + if (mWebViewController.shouldCaptureThumbnails()) { + synchronized (Tab.this) { + if (mCapture == null) { + mCapture = Bitmap.createBitmap(mCaptureWidth, mCaptureHeight, + Bitmap.Config.RGB_565); + if (mInForeground) { + postCapture(); + } + } + } + } else { + synchronized (Tab.this) { + mCapture = null; + deleteThumbnail(); + } + } } public void setController(WebViewController ctl) { mWebViewController = ctl; + updateShouldCaptureThumbnails(); } public void setId(long id) { @@ -1435,6 +1475,13 @@ class Tab implements PictureListener { mWebViewController.onSetWebView(this, w); + if (mMainView != null) { + if (w != null) { + syncCurrentState(w, null); + } else { + mCurrentState = new PageState(mContext, false); + } + } // set the new one mMainView = w; // attach the WebViewClient, WebChromeClient and DownloadListener @@ -1448,6 +1495,10 @@ class Tab implements PictureListener { mMainView.setDownloadListener(mDownloadListener); mMainView.setWebBackForwardListClient(mWebBackForwardListClient); mMainView.setPictureListener(this); + if (mSavedState != null) { + mMainView.restoreState(mSavedState); + mSavedState = null; + } } } @@ -1480,6 +1531,7 @@ class Tab implements PictureListener { if (mParent != null) { mParent.mChildren.remove(this); } + deleteThumbnail(); } /** @@ -1739,7 +1791,10 @@ class Tab implements PictureListener { * Get the favicon of this tab. */ Bitmap getFavicon() { - return mCurrentState.mFavicon; + if (mCurrentState.mFavicon != null) { + return mCurrentState.mFavicon; + } + return getDefaultFavicon(mContext); } public boolean isBookmarkedSite() { @@ -1796,43 +1851,19 @@ class Tab implements PictureListener { } /** - * Get the cached saved state bundle. - * @return cached state bundle + * @return The Bundle with the tab's state if it can be saved, otherwise null */ - Bundle getSavedState() { - return mSavedState; - } - - Bundle getSavedState(boolean saveImages) { - if (saveImages && mCapture != null) { - Bundle b = new Bundle(mSavedState); - b.putParcelable(SCREENSHOT, mCapture); - return b; - } - return mSavedState; - } - - /** - * Set the saved state. - */ - void setSavedState(Bundle state) { - mSavedState = state; - } - - /** - * @return TRUE if succeed in saving the state. - */ - boolean saveState() { + public Bundle saveState() { // If the WebView is null it means we ran low on memory and we already // stored the saved state in mSavedState. if (mMainView == null) { - return mSavedState != null; + return mSavedState; } // If the tab is the homepage or has no URL, don't save it String homepage = BrowserSettings.getInstance().getHomePage(); if (TextUtils.equals(homepage, mCurrentState.mUrl) || TextUtils.isEmpty(mCurrentState.mUrl)) { - return false; + return null; } mSavedState = new Bundle(); @@ -1841,6 +1872,7 @@ class Tab implements PictureListener { mSavedState.putLong(ID, mId); mSavedState.putString(CURRURL, mCurrentState.mUrl); mSavedState.putString(CURRTITLE, mCurrentState.mTitle); + mSavedState.putBoolean(INCOGNITO, mMainView.isPrivateBrowsingEnabled()); if (mAppId != null) { mSavedState.putString(APPID, mAppId); } @@ -1850,35 +1882,35 @@ class Tab implements PictureListener { } mSavedState.putBoolean(USERAGENT, mSettings.hasDesktopUseragent(getWebView())); - return true; + return mSavedState; } /* * Restore the state of the tab. */ - boolean restoreState(Bundle b) { - if (b == null) { - return false; + private void restoreState(Bundle b) { + mSavedState = b; + if (mSavedState == null) { + return; } // Restore the internal state even if the WebView fails to restore. // This will maintain the app id, original url and close-on-exit values. - mSavedState = null; mId = b.getLong(ID); mAppId = b.getString(APPID); - final Bitmap sshot = b.getParcelable(SCREENSHOT); - if (sshot != null) { - mCapture = sshot; - } if (b.getBoolean(USERAGENT) != mSettings.hasDesktopUseragent(getWebView())) { mSettings.toggleDesktopUseragent(getWebView()); } - - final WebBackForwardList list = mMainView.restoreState(b); - if (list == null) { - return false; + String url = b.getString(CURRURL); + String title = b.getString(CURRTITLE); + boolean incognito = b.getBoolean(INCOGNITO); + mCurrentState = new PageState(mContext, incognito, url, null); + mCurrentState.mTitle = title; + synchronized (Tab.this) { + if (mCapture != null) { + BackgroundHandler.execute(mLoadThumbnail); + } } - return true; } public void updateBookmarkedStatus() { @@ -1896,12 +1928,10 @@ class Tab implements PictureListener { } }; - public void setScreenshot(Bitmap screenshot) { - mCapture = screenshot; - } - public Bitmap getScreenshot() { - return mCapture; + synchronized (Tab.this) { + return mCapture; + } } public boolean isSnapshot() { @@ -1963,11 +1993,16 @@ class Tab implements PictureListener { float scale = mCaptureWidth / (float) mMainView.getWidth(); c.scale(scale, scale, left, top); mMainView.draw(c); + persistThumbnail(); } @Override public void onNewPicture(WebView view, Picture picture) { //update screenshot + postCapture(); + } + + private void postCapture() { if (!mHandler.hasMessages(MSG_CAPTURE)) { mHandler.sendEmptyMessageDelayed(MSG_CAPTURE, CAPTURE_DELAY); } @@ -1993,4 +2028,84 @@ class Tab implements PictureListener { } } + protected void persistThumbnail() { + BackgroundHandler.execute(mSaveThumbnail); + } + + protected void deleteThumbnail() { + BackgroundHandler.execute(mDeleteThumbnail); + } + + private void updateCaptureFromBlob(byte[] blob) { + synchronized (Tab.this) { + if (mCapture == null) { + return; + } + mCapture.copyPixelsFromBuffer(ByteBuffer.wrap(blob)); + } + } + + private byte[] getCaptureBlob() { + synchronized (Tab.this) { + if (mCapture == null) { + return null; + } + ByteBuffer buffer = ByteBuffer.allocate(mCapture.getByteCount()); + mCapture.copyPixelsToBuffer(buffer); + return buffer.array(); + } + } + + private Runnable mSaveThumbnail = new Runnable() { + + @Override + public void run() { + byte[] blob = getCaptureBlob(); + if (blob == null) { + return; + } + ContentResolver cr = mContext.getContentResolver(); + ContentValues values = new ContentValues(); + values.put(Thumbnails._ID, mId); + values.put(Thumbnails.THUMBNAIL, blob); + cr.insert(Thumbnails.CONTENT_URI, values); + } + }; + + private Runnable mDeleteThumbnail = new Runnable() { + + @Override + public void run() { + ContentResolver cr = mContext.getContentResolver(); + try { + cr.delete(ContentUris.withAppendedId(Thumbnails.CONTENT_URI, mId), + null, null); + } catch (Throwable t) {} + } + }; + + private Runnable mLoadThumbnail = new Runnable() { + + @Override + public void run() { + ContentResolver cr = mContext.getContentResolver(); + Cursor c = null; + try { + Uri uri = ContentUris.withAppendedId(Thumbnails.CONTENT_URI, mId); + c = cr.query(uri, new String[] {Thumbnails._ID, + Thumbnails.THUMBNAIL}, null, null, null); + if (c.moveToFirst()) { + byte[] data = c.getBlob(1); + if (data != null && data.length > 0) { + updateCaptureFromBlob(data); + } + } + } finally { + if (c != null) { + c.close(); + } + } + } + }; + } diff --git a/src/com/android/browser/TabControl.java b/src/com/android/browser/TabControl.java index cd8da2e..b708841 100644 --- a/src/com/android/browser/TabControl.java +++ b/src/com/android/browser/TabControl.java @@ -186,6 +186,12 @@ class TabControl { * number of open tabs. */ Tab createNewTab(boolean privateBrowsing) { + return createNewTab(null, privateBrowsing); + } + + Tab createNewTab(Bundle state, boolean privateBrowsing) { + int size = mTabs.size(); + // Return false if we have maxed out on tabs if (!canCreateNewTab()) { return null; } @@ -193,7 +199,7 @@ class TabControl { final WebView w = createNewWebView(privateBrowsing); // Create a new tab and add it to the tab list - Tab t = new Tab(mController, w); + Tab t = new Tab(mController, w, state); t.setId(getNextId()); mTabs.add(t); // Initially put the tab in the background. @@ -288,7 +294,7 @@ class TabControl { * @param outState * @param saveImages */ - void saveState(Bundle outState, boolean saveImages) { + void saveState(Bundle outState) { final int numTabs = getTabCount(); if (numTabs == 0) { return; @@ -296,10 +302,10 @@ class TabControl { long[] ids = new long[numTabs]; int i = 0; for (Tab tab : mTabs) { - if (tab.saveState()) { + Bundle tabState = tab.saveState(); + if (tabState != null) { ids[i++] = tab.getId(); - outState.putBundle(Long.toString(tab.getId()), - tab.getSavedState(saveImages)); + outState.putBundle(Long.toString(tab.getId()), tabState); } else { ids[i++] = -1; } @@ -329,7 +335,7 @@ class TabControl { final long oldcurrent = inState.getLong(CURRENT); long current = -1; if (restoreIncognitoTabs || (hasState(oldcurrent, inState) && !isIncognito(oldcurrent, inState))) { - current = oldcurrent; + current = oldcurrent; } else { // pick first non incognito tab for (long id : ids) { @@ -363,8 +369,6 @@ class TabControl { * @param restoreIncognitoTabs Restoring private browsing tabs * @param restoreAll All webviews get restored, not just the current tab * (this does not override handling of incognito tabs) - * @return True if there were previous tabs that were restored. False if - * there was no saved state or restoring the state failed. */ void restoreState(Bundle inState, long currentId, boolean restoreIncognitoTabs, boolean restoreAll) { @@ -387,7 +391,7 @@ class TabControl { && state.getBoolean(Tab.INCOGNITO)) { // ignore tab } else if (id == currentId || restoreAll) { - Tab t = createNewTab(); + Tab t = createNewTab(state, false); if (t == null) { // We could "break" at this point, but we want // sNextId to be set correctly. @@ -399,23 +403,12 @@ class TabControl { if (id == currentId) { setCurrentTab(t); } - if (!t.restoreState(state)) { - Log.w(LOGTAG, "Fail in restoreState, load home page."); - t.getWebView().loadUrl(BrowserSettings.getInstance() - .getHomePage()); - } } else { // Create a new tab and don't restore the state yet, add it // to the tab list - Tab t = new Tab(mController, null); + Tab t = new Tab(mController, state); t.setId(id); tabMap.put(id, t); - if (state != null) { - t.setSavedState(state); - // Need to maintain the app id and original url so we - // can possibly reuse this tab. - t.setAppId(state.getString(Tab.APPID)); - } mTabs.add(t); // added the tab to the front as they are not current mTabQueue.add(0, t); @@ -619,8 +612,6 @@ class TabControl { if (getCurrentTab() == t) { setCurrentTab(t, true); } - // Clear the saved state and picker data - t.setSavedState(null); } /** @@ -681,12 +672,6 @@ class TabControl { newTab.setWebView(mainView); } newTab.putInForeground(); - if (needRestore) { - // Have to finish setCurrentTab work before calling restoreState - if (!newTab.restoreState(newTab.getSavedState())) { - mainView.loadUrl(BrowserSettings.getInstance().getHomePage()); - } - } return true; } diff --git a/src/com/android/browser/UI.java b/src/com/android/browser/UI.java index 23897f7..2984d5c 100644 --- a/src/com/android/browser/UI.java +++ b/src/com/android/browser/UI.java @@ -147,4 +147,6 @@ public interface UI { void setUseQuickControls(boolean enabled); + public boolean shouldCaptureThumbnails(); + } diff --git a/src/com/android/browser/WebViewController.java b/src/com/android/browser/WebViewController.java index 175cbf8..f4ff764 100644 --- a/src/com/android/browser/WebViewController.java +++ b/src/com/android/browser/WebViewController.java @@ -122,4 +122,6 @@ public interface WebViewController { void showAutoLogin(Tab tab); void hideAutoLogin(Tab tab); + + boolean shouldCaptureThumbnails(); } diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java index 73821bf..aeba7cf 100644 --- a/src/com/android/browser/XLargeUi.java +++ b/src/com/android/browser/XLargeUi.java @@ -101,6 +101,10 @@ public class XLargeUi extends BaseUi { setTitleGravity(Gravity.NO_GRAVITY); } mTabBar.setUseQuickControls(mUseQuickControls); + // We need to update the tabs with this change + for (Tab t : mTabControl.getTabs()) { + t.updateShouldCaptureThumbnails(); + } } private void checkTabCount() { @@ -334,4 +338,9 @@ public class XLargeUi extends BaseUi { return mTabBar; } + @Override + public boolean shouldCaptureThumbnails() { + return mUseQuickControls; + } + } diff --git a/src/com/android/browser/provider/BrowserProvider2.java b/src/com/android/browser/provider/BrowserProvider2.java index b974c0e..e40a882 100644 --- a/src/com/android/browser/provider/BrowserProvider2.java +++ b/src/com/android/browser/provider/BrowserProvider2.java @@ -74,6 +74,13 @@ public class BrowserProvider2 extends SQLiteContentProvider { static final Uri LEGACY_AUTHORITY_URI = new Uri.Builder() .authority(LEGACY_AUTHORITY).scheme("content").build(); + public static interface Thumbnails { + public static final Uri CONTENT_URI = Uri.withAppendedPath( + BrowserContract.AUTHORITY_URI, "thumbnails"); + public static final String _ID = "_id"; + public static final String THUMBNAIL = "thumbnail"; + } + static final String TABLE_BOOKMARKS = "bookmarks"; static final String TABLE_HISTORY = "history"; static final String TABLE_IMAGES = "images"; @@ -81,6 +88,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { static final String TABLE_SYNC_STATE = "syncstate"; static final String TABLE_SETTINGS = "settings"; static final String TABLE_SNAPSHOTS = "snapshots"; + static final String TABLE_THUMBNAILS = "thumbnails"; static final String TABLE_BOOKMARKS_JOIN_IMAGES = "bookmarks LEFT OUTER JOIN images " + "ON bookmarks.url = images." + Images.URL; @@ -111,6 +119,9 @@ public class BrowserProvider2 extends SQLiteContentProvider { "WHERE url IS NOT NULL AND deleted == 0) AND url_key NOT IN " + "(SELECT url FROM history WHERE url IS NOT NULL)"; + static final int THUMBNAILS = 10; + static final int THUMBNAILS_ID = 11; + static final int BOOKMARKS = 1000; static final int BOOKMARKS_ID = 1001; static final int BOOKMARKS_FOLDER = 1002; @@ -187,6 +198,8 @@ public class BrowserProvider2 extends SQLiteContentProvider { matcher.addURI(authority, "combined", COMBINED); matcher.addURI(authority, "combined/#", COMBINED_ID); matcher.addURI(authority, "settings", SETTINGS); + matcher.addURI(authority, "thumbnails", THUMBNAILS); + matcher.addURI(authority, "thumbnails/#", THUMBNAILS_ID); // Legacy matcher.addURI(LEGACY_AUTHORITY, "searches", SEARCHES); @@ -333,7 +346,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { final class DatabaseHelper extends SQLiteOpenHelper { static final String DATABASE_NAME = "browser2.db"; - static final int DATABASE_VERSION = 30; + static final int DATABASE_VERSION = 31; public DatabaseHelper(Context context) { super(context, DATABASE_NAME, null, DATABASE_VERSION); } @@ -396,6 +409,7 @@ public class BrowserProvider2 extends SQLiteContentProvider { ");"); createAccountsView(db); + createThumbnails(db); mSyncHelper.createDatabase(db); @@ -406,6 +420,13 @@ public class BrowserProvider2 extends SQLiteContentProvider { enableSync(db); } + void createThumbnails(SQLiteDatabase db) { + db.execSQL("CREATE TABLE IF NOT EXISTS " + TABLE_THUMBNAILS + " (" + + Thumbnails._ID + " INTEGER PRIMARY KEY," + + Thumbnails.THUMBNAIL + " BLOB NOT NULL" + + ");"); + } + void enableSync(SQLiteDatabase db) { ContentValues values = new ContentValues(); values.put(Settings.KEY, Settings.KEY_SYNC_ENABLED); @@ -500,6 +521,9 @@ public class BrowserProvider2 extends SQLiteContentProvider { @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { + if (oldVersion < 31) { + createThumbnails(db); + } if (oldVersion < 30) { db.execSQL("DROP VIEW IF EXISTS " + VIEW_SNAPSHOTS_COMBINED); db.execSQL("DROP TABLE IF EXISTS " + TABLE_SNAPSHOTS); @@ -974,6 +998,18 @@ public class BrowserProvider2 extends SQLiteContentProvider { break; } + case THUMBNAILS_ID: { + selection = DatabaseUtils.concatenateWhere( + selection, Thumbnails._ID + " = ?"); + selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, + new String[] { Long.toString(ContentUris.parseId(uri)) }); + // fall through + } + case THUMBNAILS: { + qb.setTables(TABLE_THUMBNAILS); + break; + } + default: { throw new UnsupportedOperationException("Unknown URL " + uri.toString()); } @@ -1173,6 +1209,17 @@ public class BrowserProvider2 extends SQLiteContentProvider { } break; } + case THUMBNAILS_ID: { + selection = DatabaseUtils.concatenateWhere( + selection, Thumbnails._ID + " = ?"); + selectionArgs = DatabaseUtils.appendSelectionArgs(selectionArgs, + new String[] { Long.toString(ContentUris.parseId(uri)) }); + // fall through + } + case THUMBNAILS: { + deleted = db.delete(TABLE_THUMBNAILS, selection, selectionArgs); + break; + } default: { throw new UnsupportedOperationException("Unknown delete URI " + uri); } @@ -1310,6 +1357,11 @@ public class BrowserProvider2 extends SQLiteContentProvider { break; } + case THUMBNAILS: { + id = db.replaceOrThrow(TABLE_THUMBNAILS, null, values); + break; + } + default: { throw new UnsupportedOperationException("Unknown insert URI " + uri); } @@ -1552,6 +1604,12 @@ public class BrowserProvider2 extends SQLiteContentProvider { break; } + case THUMBNAILS: { + modified = db.update(TABLE_THUMBNAILS, values, + selection, selectionArgs); + break; + } + default: { throw new UnsupportedOperationException("Unknown update URI " + uri); } |