Always offer to restore

 Always offer to restore (even across reboots)
 Periodically save while the user is typing in a textfield

Change-Id: If226ea7d2e19c58d8205d663f9f8dd670c712436
diff --git a/src/com/android/browser/BrowserWebView.java b/src/com/android/browser/BrowserWebView.java
index 5a40b3c..55dd24a 100644
--- a/src/com/android/browser/BrowserWebView.java
+++ b/src/com/android/browser/BrowserWebView.java
@@ -191,4 +191,13 @@
         }
     }
 
+    @Override
+    protected void updateCachedTextfield(String updatedText) {
+        super.updateCachedTextfield(updatedText);
+        CrashRecoveryHandler handler = CrashRecoveryHandler.getInstance();
+        if (handler != null) {
+            handler.backupState();
+        }
+    }
+
 }
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 986b617..5fb413b 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -226,7 +226,7 @@
         mDataController = DataController.getInstance(mActivity);
         mTabControl = new TabControl(this);
         mSettings.setController(this);
-        mCrashRecoveryHandler = new CrashRecoveryHandler(this);
+        mCrashRecoveryHandler = CrashRecoveryHandler.initialize(this);
 
         mUrlHandler = new UrlHandler(this);
         mIntentHandler = new IntentHandler(mActivity, this);
@@ -262,7 +262,6 @@
     void start(final Bundle icicle, final Intent intent) {
         boolean noCrashRecovery = intent.getBooleanExtra(NO_CRASH_RECOVERY, false);
         if (icicle != null || noCrashRecovery) {
-            mCrashRecoveryHandler.clearState();
             doStart(icicle, intent);
         } else {
             mCrashRecoveryHandler.startRecovery(intent);
@@ -615,7 +614,7 @@
         mNetworkHandler.onPause();
 
         WebView.disablePlatformNotifications();
-        mCrashRecoveryHandler.clearState();
+        mCrashRecoveryHandler.backupState();
     }
 
     void onSaveInstanceState(Bundle outState, boolean saveImages) {
@@ -2141,6 +2140,7 @@
     protected void removeTab(Tab tab) {
         mUi.removeTab(tab);
         mTabControl.removeTab(tab);
+        mCrashRecoveryHandler.backupState();
     }
 
     @Override
diff --git a/src/com/android/browser/CrashRecoveryHandler.java b/src/com/android/browser/CrashRecoveryHandler.java
index 60e39da..7ee9cee 100644
--- a/src/com/android/browser/CrashRecoveryHandler.java
+++ b/src/com/android/browser/CrashRecoveryHandler.java
@@ -22,7 +22,10 @@
 import android.content.DialogInterface.OnClickListener;
 import android.content.Intent;
 import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
 import android.os.Parcel;
+import android.os.Process;
 import android.util.Log;
 
 import java.io.ByteArrayOutputStream;
@@ -35,37 +38,88 @@
     private static final String LOGTAG = "BrowserCrashRecovery";
     private static final String STATE_FILE = "browser_state.parcel";
     private static final int BUFFER_SIZE = 4096;
+    private static final long BACKUP_DELAY = 500; // 500ms between writes
+
+    private static CrashRecoveryHandler sInstance;
 
     private Controller mController;
+    private Handler mForegroundHandler;
+    private Handler mBackgroundHandler;
 
-    public CrashRecoveryHandler(Controller controller) {
+    public static CrashRecoveryHandler initialize(Controller controller) {
+        if (sInstance == null) {
+            sInstance = new CrashRecoveryHandler(controller);
+        } else {
+            sInstance.mController = controller;
+        }
+        return sInstance;
+    }
+
+    public static CrashRecoveryHandler getInstance() {
+        return sInstance;
+    }
+
+    private CrashRecoveryHandler(Controller controller) {
         mController = controller;
+        mForegroundHandler = new Handler();
+        HandlerThread thread = new HandlerThread(LOGTAG,
+                Process.THREAD_PRIORITY_BACKGROUND);
+        thread.start();
+        mBackgroundHandler = new Handler(thread.getLooper());
     }
 
     public void backupState() {
-        final Bundle state = new Bundle();
-        mController.onSaveInstanceState(state, false);
-        final Context context = mController.getActivity();
-        new Thread() {
-            @Override
-            public void run() {
-                Parcel p = Parcel.obtain();
-                try {
-                    state.writeToParcel(p, 0);
-                    FileOutputStream fout = context.openFileOutput(STATE_FILE,
-                            Context.MODE_PRIVATE);
-                    fout.write(p.marshall());
-                    fout.close();
-                } catch (Throwable e) {
-                    Log.i(LOGTAG, "Failed to save persistent state", e);
-                } finally {
-                    p.recycle();
-                }
-            }
-        }.start();
+        mForegroundHandler.postDelayed(mCreateState, BACKUP_DELAY);
     }
 
-    public void clearState() {
+    private Runnable mCreateState = new Runnable() {
+
+        @Override
+        public void run() {
+            try {
+                final Bundle state = new Bundle();
+                mController.onSaveInstanceState(state, false);
+                Context context = mController.getActivity()
+                        .getApplicationContext();
+                mBackgroundHandler.post(new WriteState(context, state));
+                // Remove any queued up saves
+                mForegroundHandler.removeCallbacks(mCreateState);
+            } catch (Throwable t) {
+                Log.w(LOGTAG, "Failed to save state", t);
+                return;
+            }
+        }
+
+    };
+
+    static class WriteState implements Runnable {
+        private Context mContext;
+        private Bundle mState;
+
+        WriteState(Context context, Bundle state) {
+            mContext = context;
+            mState = state;
+        }
+
+        @Override
+        public void run() {
+            Parcel p = Parcel.obtain();
+            try {
+                mState.writeToParcel(p, 0);
+                FileOutputStream fout = mContext.openFileOutput(STATE_FILE,
+                        Context.MODE_PRIVATE);
+                fout.write(p.marshall());
+                fout.close();
+            } catch (Throwable e) {
+                Log.i(LOGTAG, "Failed to save persistent state", e);
+            } finally {
+                p.recycle();
+            }
+        }
+
+    }
+
+    private void clearState() {
         Context context = mController.getActivity();
         context.deleteFile(STATE_FILE);
     }