Freeze tab improvements

Change-Id: I5d5e5a7a18cafdbe845fa1ef949276bdfd1996d3
diff --git a/res/drawable-hdpi/ic_snapshot.png b/res/drawable-hdpi/ic_snapshot.png
new file mode 100644
index 0000000..a621cb4
--- /dev/null
+++ b/res/drawable-hdpi/ic_snapshot.png
Binary files differ
diff --git a/res/drawable-mdpi/ic_snapshot.png b/res/drawable-mdpi/ic_snapshot.png
new file mode 100644
index 0000000..6054acb
--- /dev/null
+++ b/res/drawable-mdpi/ic_snapshot.png
Binary files differ
diff --git a/res/layout/tab_title.xml b/res/layout/tab_title.xml
index e2c6d0a..fcae2bc 100644
--- a/res/layout/tab_title.xml
+++ b/res/layout/tab_title.xml
@@ -31,6 +31,13 @@
         android:src="@drawable/ic_incognito_holo_dark"
         android:visibility="gone" />
     <ImageView
+        android:id="@+id/snapshot"
+        android:layout_width="wrap_content"
+        android:layout_height="match_parent"
+        android:gravity="center_vertical"
+        android:src="@drawable/ic_snapshot"
+        android:visibility="gone" />
+    <ImageView
         android:id="@+id/favicon"
         android:layout_width="20dip"
         android:layout_height="20dip"
diff --git a/src/com/android/browser/Controller.java b/src/com/android/browser/Controller.java
index 6a951c2..986b617 100644
--- a/src/com/android/browser/Controller.java
+++ b/src/com/android/browser/Controller.java
@@ -1496,7 +1496,6 @@
                 final MenuItem counter = menu.findItem(R.id.dump_counters_menu_id);
                 counter.setVisible(showDebugSettings);
                 counter.setEnabled(showDebugSettings);
-                menu.findItem(R.id.freeze_tab_menu_id).setVisible(showDebugSettings);
 
                 final MenuItem newtab = menu.findItem(R.id.new_tab_menu_id);
                 newtab.setEnabled(getTabControl().canCreateNewTab());
@@ -1605,21 +1604,20 @@
 
             case R.id.freeze_tab_menu_id:
                 // TODO: Show error messages
-                WebView source = getCurrentTopWebView();
+                Tab source = getTabControl().getCurrentTab();
                 if (source == null) break;
-                Tab t = createNewTab(false, true, false);
-                if (t == null) break;
-                WebView pinned = t.getWebView();
-                if (pinned == null) break;
+                Tab snapshot = createNewTab(false, false, false);
+                if (snapshot == null) break;
                 try {
                     ByteArrayOutputStream bos = new ByteArrayOutputStream();
-                    source.saveViewState(bos);
+                    source.saveSnapshot(bos);
                     ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());
-                    pinned.loadViewState(bis);
+                    snapshot.loadSnapshot(bis);
+                    mUi.onTabDataChanged(snapshot);
                     bis.close();
                     bos.close();
+                    setActiveTab(snapshot);
                 } catch (IOException e) {
-                    closeTab(t);
                 }
                 break;
 
diff --git a/src/com/android/browser/Tab.java b/src/com/android/browser/Tab.java
index b0a991a..95c7850 100644
--- a/src/com/android/browser/Tab.java
+++ b/src/com/android/browser/Tab.java
@@ -16,9 +16,6 @@
 
 package com.android.browser;
 
-import com.android.browser.homepages.HomeProvider;
-import com.android.common.speech.LoggingEvents;
-
 import android.app.Activity;
 import android.app.AlertDialog;
 import android.app.SearchManager;
@@ -28,6 +25,7 @@
 import android.content.DialogInterface.OnCancelListener;
 import android.content.Intent;
 import android.graphics.Bitmap;
+import android.graphics.Bitmap.CompressFormat;
 import android.graphics.BitmapFactory;
 import android.net.Uri;
 import android.net.http.SslError;
@@ -60,6 +58,15 @@
 import android.widget.TextView;
 import android.widget.Toast;
 
+import com.android.browser.homepages.HomeProvider;
+import com.android.common.speech.LoggingEvents;
+
+import java.io.ByteArrayOutputStream;
+import java.io.DataInputStream;
+import java.io.DataOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.Iterator;
@@ -135,6 +142,7 @@
     private DataController mDataController;
     // State of the auto-login request.
     private DeviceAccountLogin mDeviceAccountLogin;
+    private boolean mIsSnapshot = false;
 
     // AsyncTask for downloading touch icons
     DownloadTouchIcon mTouchIconLoader;
@@ -1475,6 +1483,10 @@
      * @param child the Tab that was created from this Tab
      */
     void addChildTab(Tab child) {
+        if (mIsSnapshot) {
+            throw new IllegalStateException(
+                    "Snapshot tabs cannot have child tabs!");
+        }
         if (mChildren == null) {
             mChildren = new Vector<Tab>();
         }
@@ -1739,10 +1751,7 @@
         }
 
         mSavedState = new Bundle();
-        final WebBackForwardList list = mMainView.saveState(mSavedState);
-
-        // Store some extra info for displaying the tab in the picker.
-        final WebHistoryItem item = list != null ? list.getCurrentItem() : null;
+        mMainView.saveState(mSavedState);
 
         mSavedState.putLong(ID, mId);
         mSavedState.putString(CURRURL, mCurrentState.mUrl);
@@ -1807,4 +1816,101 @@
         return mScreenshot;
     }
 
+    public boolean isSnapshot() {
+        return mIsSnapshot;
+    }
+
+    public boolean loadSnapshot(InputStream rstream) {
+        if (rstream == null) {
+            mIsSnapshot = false;
+            if (mMainView != null) {
+                mMainView.clearViewState();
+            }
+            return true;
+        }
+        DataInputStream stream = new DataInputStream(rstream);
+        if (!readTabInfo(stream)) {
+            return false;
+        }
+        if (!mMainView.loadViewState(stream)) {
+            return false;
+        }
+        mIsSnapshot = true;
+        return true;
+    }
+
+    public boolean saveSnapshot(OutputStream rstream) {
+        if (rstream == null) return false;
+        if (mMainView == null) return false;
+        DataOutputStream stream = new DataOutputStream(rstream);
+        if (saveTabInfo(stream)) {
+            return mMainView.saveViewState(stream);
+        }
+        return false;
+    }
+
+    private boolean readTabInfo(DataInputStream stream) {
+        try {
+            PageState state = new PageState(mActivity, false);
+            state.mTitle = stream.readUTF();
+            if (state.mTitle.length() == 0) {
+                state.mTitle = null;
+            }
+            state.mUrl = stream.readUTF();
+            int faviconLen = stream.readInt();
+            if (faviconLen > 0) {
+                byte[] data = new byte[faviconLen];
+                int read = stream.read(data);
+                if (read != faviconLen) {
+                    throw new IOException("Read didn't match expected len!"
+                            + " Expected: " + faviconLen
+                            + " Got: " + read);
+                }
+                state.mFavicon = BitmapFactory.decodeByteArray(data, 0, data.length);
+            }
+            mCurrentState = state;
+            return true;
+        } catch (IOException e) {
+            return false;
+        }
+    }
+
+    private boolean saveTabInfo(DataOutputStream stream) {
+        try {
+            // mTitle might be null, but writeUTF doesn't handle that
+            String title = mCurrentState.mTitle;
+            stream.writeUTF(title != null ? title : "");
+            // mUrl is never null
+            stream.writeUTF(mCurrentState.mUrl);
+            byte[] compressedPixels = compressFavicon();
+            if (compressedPixels == null) {
+                stream.writeInt(-1);
+            } else {
+                stream.writeInt(compressedPixels.length);
+                stream.write(compressedPixels);
+            }
+            return true;
+        } catch (Exception e) {
+            Log.w(LOGTAG, "Failed to saveTabInfo", e);
+            return false;
+        }
+    }
+
+    private byte[] compressFavicon() {
+        Bitmap favicon = mCurrentState.mFavicon;
+        if (favicon == null) {
+            return null;
+        }
+        ByteArrayOutputStream stream = new ByteArrayOutputStream();
+        byte[] data = null;
+        try {
+            favicon.compress(CompressFormat.PNG, 100, stream);
+            data = stream.toByteArray();
+            stream.close();
+        } catch (IOException e) {
+            // Will return null below then
+        }
+        return data;
+    }
+
 }
diff --git a/src/com/android/browser/TabBar.java b/src/com/android/browser/TabBar.java
index 6c3949a..6e84a40 100644
--- a/src/com/android/browser/TabBar.java
+++ b/src/com/android/browser/TabBar.java
@@ -238,7 +238,7 @@
 
     void showTitleBarIndicator(boolean show) {
         Tab tab = mTabControl.getCurrentTab();
-        if (tab != null) {
+        if (tab != null && !tab.isSnapshot()) {
             TabView tv = mTabMap.get(tab);
             if (tv != null) {
                 tv.showIndicator(show);
@@ -325,6 +325,7 @@
         TextView mTitle;
         View mIndicator;
         View mIncognito;
+        View mSnapshot;
         ImageView mIconView;
         ImageView mLock;
         ImageView mClose;
@@ -355,6 +356,7 @@
             mClose = (ImageView) mTabContent.findViewById(R.id.close);
             mClose.setOnClickListener(this);
             mIncognito = mTabContent.findViewById(R.id.incognito);
+            mSnapshot = mTabContent.findViewById(R.id.snapshot);
             mIndicator = mTabContent.findViewById(R.id.chevron);
             mSelected = false;
             mInLoad = false;
@@ -399,11 +401,15 @@
             if (mTab.getFavicon() != null) {
                 setFavicon(renderFavicon(mTab.getFavicon()));
             }
-            if (mTab != null) {
-                mIncognito.setVisibility(
-                        mTab.isPrivateBrowsingEnabled() ?
-                        View.VISIBLE : View.GONE);
-            }
+            updateTabIcons();
+        }
+
+        private void updateTabIcons() {
+            mIncognito.setVisibility(
+                    mTab.isPrivateBrowsingEnabled() ?
+                    View.VISIBLE : View.GONE);
+            mSnapshot.setVisibility(mTab.isSnapshot()
+                    ? View.VISIBLE : View.GONE);
         }
 
         @Override
@@ -666,6 +672,7 @@
             } else if (url != null) {
                 tv.setDisplayTitle(UrlUtils.stripUrl(url));
             }
+            tv.updateTabIcons();
         }
     }
 
diff --git a/src/com/android/browser/XLargeUi.java b/src/com/android/browser/XLargeUi.java
index 1530ed3..25cdbd9 100644
--- a/src/com/android/browser/XLargeUi.java
+++ b/src/com/android/browser/XLargeUi.java
@@ -213,7 +213,7 @@
             mTabBar.showTitleBarIndicator(false);
         } else {
             // check if title bar is already attached by animation
-            if (mTitleBar.getParent() == null) {
+            if (mTitleBar.getParent() == null && !tab.isSnapshot()) {
                 view.setEmbeddedTitleBar(mTitleBar);
             }
             view.setScrollListener(this);