Save and load snapshots async

 Bug: 5416822

Change-Id: I213c3507af61e7ca0354dad7e72ece7a2547f54e
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index fcbe387..05ccdd2 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -17,7 +17,10 @@
 package com.android.browser;
 
 import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
 import android.app.DownloadManager;
+import android.app.ProgressDialog;
 import android.app.SearchManager;
 import android.content.ClipboardManager;
 import android.content.ContentProvider;
@@ -26,6 +29,8 @@
 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.content.pm.PackageManager;
 import android.content.pm.ResolveInfo;
@@ -1633,37 +1638,7 @@
             case R.id.save_snapshot_menu_id:
                 final Tab source = getTabControl().getCurrentTab();
                 if (source == null) break;
-                final ContentResolver cr = mActivity.getContentResolver();
-                final ContentValues values = source.createSnapshotValues();
-                if (values != null) {
-                    new AsyncTask<Tab, Void, Long>() {
-
-                        @Override
-                        protected Long doInBackground(Tab... params) {
-                            Uri result = cr.insert(Snapshots.CONTENT_URI, values);
-                            if (result == null) {
-                                return null;
-                            }
-                            long id = ContentUris.parseId(result);
-                            return id;
-                        }
-
-                        @Override
-                        protected void onPostExecute(Long id) {
-                            if (id == null) {
-                                Toast.makeText(mActivity, R.string.snapshot_failed,
-                                        Toast.LENGTH_SHORT).show();
-                                return;
-                            }
-                            Bundle b = new Bundle();
-                            b.putLong(BrowserSnapshotPage.EXTRA_ANIMATE_ID, id);
-                            mUi.showComboView(ComboViews.Snapshots, b);
-                        };
-                    }.execute(source);
-                } else {
-                    Toast.makeText(mActivity, R.string.snapshot_failed,
-                            Toast.LENGTH_SHORT).show();
-                }
+                new SaveSnapshotTask(source).execute();
                 break;
 
             case R.id.page_info_menu_id:
@@ -1731,6 +1706,69 @@
         return true;
     }
 
+    private class SaveSnapshotTask extends AsyncTask<Void, Void, Long>
+            implements OnCancelListener {
+
+        private Tab mTab;
+        private Dialog mProgressDialog;
+        private ContentValues mValues;
+
+        private SaveSnapshotTask(Tab tab) {
+            mTab = tab;
+        }
+
+        @Override
+        protected void onPreExecute() {
+            CharSequence message = mActivity.getText(R.string.saving_snapshot);
+            mProgressDialog = ProgressDialog.show(mActivity, null, message,
+                    true, true, this);
+            mValues = mTab.createSnapshotValues();
+        }
+
+        @Override
+        protected Long doInBackground(Void... params) {
+            if (!mTab.saveViewState(mValues)) {
+                return null;
+            }
+            if (isCancelled()) {
+                String path = mValues.getAsString(Snapshots.VIEWSTATE_PATH);
+                File file = mActivity.getFileStreamPath(path);
+                if (!file.delete()) {
+                    file.deleteOnExit();
+                }
+                return null;
+            }
+            final ContentResolver cr = mActivity.getContentResolver();
+            Uri result = cr.insert(Snapshots.CONTENT_URI, mValues);
+            if (result == null) {
+                return null;
+            }
+            long id = ContentUris.parseId(result);
+            return id;
+        }
+
+        @Override
+        protected void onPostExecute(Long id) {
+            if (isCancelled()) {
+                return;
+            }
+            mProgressDialog.dismiss();
+            if (id == null) {
+                Toast.makeText(mActivity, R.string.snapshot_failed,
+                        Toast.LENGTH_SHORT).show();
+                return;
+            }
+            Bundle b = new Bundle();
+            b.putLong(BrowserSnapshotPage.EXTRA_ANIMATE_ID, id);
+            mUi.showComboView(ComboViews.Snapshots, b);
+        }
+
+        @Override
+        public void onCancel(DialogInterface dialog) {
+            cancel(true);
+        }
+    }
+
     @Override
     public void toggleUserAgent() {
         WebView web = getCurrentWebView();
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index 04bee08..c73bdf6 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -77,6 +77,7 @@
 
 import java.io.ByteArrayOutputStream;
 import java.io.File;
+import java.io.IOException;
 import java.io.OutputStream;
 import java.nio.ByteBuffer;
 import java.util.ArrayList;
@@ -2060,29 +2061,29 @@
         return false;
     }
 
-    public ContentValues createSnapshotValues() {
-        if (mMainView == null) return null;
-        String path = UUID.randomUUID().toString();
-        try {
-            OutputStream outs = mContext.openFileOutput(path, Context.MODE_PRIVATE);
-            GZIPOutputStream stream = new GZIPOutputStream(outs);
-            if (!getWebViewClassic().saveViewState(stream)) {
-                return null;
+    private static class SaveCallback implements ValueCallback<Boolean> {
+        boolean mResult;
+
+        @Override
+        public void onReceiveValue(Boolean value) {
+            mResult = value;
+            synchronized (this) {
+                notifyAll();
             }
-            stream.flush();
-            stream.close();
-        } catch (Exception e) {
-            Log.w(LOGTAG, "Failed to save view state", e);
-            return null;
         }
-        File savedFile = mContext.getFileStreamPath(path);
-        long size = savedFile.length();
+
+    }
+
+    /**
+     * Must be called on the UI thread
+     */
+    public ContentValues createSnapshotValues() {
+        WebViewClassic web = getWebViewClassic();
+        if (web == null) return null;
         ContentValues values = new ContentValues();
         values.put(Snapshots.TITLE, mCurrentState.mTitle);
         values.put(Snapshots.URL, mCurrentState.mUrl);
-        values.put(Snapshots.VIEWSTATE_PATH, path);
-        values.put(Snapshots.VIEWSTATE_SIZE, size);
-        values.put(Snapshots.BACKGROUND, getWebViewClassic().getPageBackgroundColor());
+        values.put(Snapshots.BACKGROUND, web.getPageBackgroundColor());
         values.put(Snapshots.DATE_CREATED, System.currentTimeMillis());
         values.put(Snapshots.FAVICON, compressBitmap(getFavicon()));
         Bitmap screenshot = Controller.createScreenshot(mMainView,
@@ -2092,6 +2093,50 @@
         return values;
     }
 
+    /**
+     * Probably want to call this on a background thread
+     */
+    public boolean saveViewState(ContentValues values) {
+        WebViewClassic web = getWebViewClassic();
+        if (web == null) return false;
+        String path = UUID.randomUUID().toString();
+        SaveCallback callback = new SaveCallback();
+        OutputStream outs = null;
+        try {
+            outs = mContext.openFileOutput(path, Context.MODE_PRIVATE);
+            GZIPOutputStream stream = new GZIPOutputStream(outs);
+            synchronized (callback) {
+                web.saveViewState(stream, callback);
+                callback.wait();
+            }
+            stream.flush();
+            stream.close();
+        } catch (Exception e) {
+            Log.w(LOGTAG, "Failed to save view state", e);
+            if (outs != null) {
+                try {
+                    outs.close();
+                } catch (IOException ignore) {}
+            }
+            File file = mContext.getFileStreamPath(path);
+            if (file.exists() && !file.delete()) {
+                file.deleteOnExit();
+            }
+            return false;
+        }
+        File savedFile = mContext.getFileStreamPath(path);
+        if (!callback.mResult) {
+            if (!savedFile.delete()) {
+                savedFile.deleteOnExit();
+            }
+            return false;
+        }
+        long size = savedFile.length();
+        values.put(Snapshots.VIEWSTATE_PATH, path);
+        values.put(Snapshots.VIEWSTATE_SIZE, size);
+        return true;
+    }
+
     public byte[] compressBitmap(Bitmap bitmap) {
         if (bitmap == null) {
             return null;