1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.browser;
17
18import android.content.ContentResolver;
19import android.content.ContentUris;
20import android.content.ContentValues;
21import android.content.Context;
22import android.database.Cursor;
23import android.graphics.BitmapFactory;
24import android.net.Uri;
25import android.os.AsyncTask;
26import android.os.Bundle;
27import android.text.TextUtils;
28import android.util.Log;
29import android.webkit.WebView;
30import android.webkit.WebViewClassic;
31
32import com.android.browser.provider.SnapshotProvider.Snapshots;
33
34import java.io.ByteArrayInputStream;
35import java.io.FileNotFoundException;
36import java.io.InputStream;
37import java.util.Map;
38import java.util.zip.GZIPInputStream;
39
40
41public class SnapshotTab extends Tab {
42
43    private static final String LOGTAG = "SnapshotTab";
44
45    private long mSnapshotId;
46    private LoadData mLoadTask;
47    private WebViewFactory mWebViewFactory;
48    private int mBackgroundColor;
49    private long mDateCreated;
50    private boolean mIsLive;
51
52    public SnapshotTab(WebViewController wvcontroller, long snapshotId) {
53        super(wvcontroller, null, null);
54        mSnapshotId = snapshotId;
55        mWebViewFactory = mWebViewController.getWebViewFactory();
56        WebView web = mWebViewFactory.createWebView(false);
57        setWebView(web);
58        loadData();
59    }
60
61    @Override
62    void putInForeground() {
63        if (getWebView() == null) {
64            WebView web = mWebViewFactory.createWebView(false);
65            if (mBackgroundColor != 0) {
66                web.setBackgroundColor(mBackgroundColor);
67            }
68            setWebView(web);
69            loadData();
70        }
71        super.putInForeground();
72    }
73
74    @Override
75    void putInBackground() {
76        if (getWebView() == null) return;
77        super.putInBackground();
78    }
79
80    void loadData() {
81        if (mLoadTask == null) {
82            mLoadTask = new LoadData(this, mContext);
83            mLoadTask.execute();
84        }
85    }
86
87    @Override
88    void addChildTab(Tab child) {
89        if (mIsLive) {
90            super.addChildTab(child);
91        } else {
92            throw new IllegalStateException("Snapshot tabs cannot have child tabs!");
93        }
94    }
95
96    @Override
97    public boolean isSnapshot() {
98        return !mIsLive;
99    }
100
101    public long getSnapshotId() {
102        return mSnapshotId;
103    }
104
105    @Override
106    public ContentValues createSnapshotValues() {
107        if (mIsLive) {
108            return super.createSnapshotValues();
109        }
110        return null;
111    }
112
113    @Override
114    public Bundle saveState() {
115        if (mIsLive) {
116            return super.saveState();
117        }
118        return null;
119    }
120
121    public long getDateCreated() {
122        return mDateCreated;
123    }
124
125    @Override
126    public void loadUrl(String url, Map<String, String> headers) {
127        if (!mIsLive) {
128            mIsLive = true;
129            getWebViewClassic().clearViewState();
130        }
131        super.loadUrl(url, headers);
132    }
133
134    @Override
135    public boolean canGoBack() {
136        return super.canGoBack() || mIsLive;
137    }
138
139    @Override
140    public boolean canGoForward() {
141        return mIsLive && super.canGoForward();
142    }
143
144    @Override
145    public void goBack() {
146        if (super.canGoBack()) {
147            super.goBack();
148        } else {
149            mIsLive = false;
150            getWebView().stopLoading();
151            loadData();
152        }
153    }
154
155    static class LoadData extends AsyncTask<Void, Void, Cursor> {
156
157        static final String[] PROJECTION = new String[] {
158            Snapshots._ID, // 0
159            Snapshots.URL, // 1
160            Snapshots.TITLE, // 2
161            Snapshots.FAVICON, // 3
162            Snapshots.VIEWSTATE, // 4
163            Snapshots.BACKGROUND, // 5
164            Snapshots.DATE_CREATED, // 6
165            Snapshots.VIEWSTATE_PATH, // 7
166        };
167        static final int SNAPSHOT_ID = 0;
168        static final int SNAPSHOT_URL = 1;
169        static final int SNAPSHOT_TITLE = 2;
170        static final int SNAPSHOT_FAVICON = 3;
171        static final int SNAPSHOT_VIEWSTATE = 4;
172        static final int SNAPSHOT_BACKGROUND = 5;
173        static final int SNAPSHOT_DATE_CREATED = 6;
174        static final int SNAPSHOT_VIEWSTATE_PATH = 7;
175
176        private SnapshotTab mTab;
177        private ContentResolver mContentResolver;
178        private Context mContext;
179
180        public LoadData(SnapshotTab t, Context context) {
181            mTab = t;
182            mContentResolver = context.getContentResolver();
183            mContext = context;
184        }
185
186        @Override
187        protected Cursor doInBackground(Void... params) {
188            long id = mTab.mSnapshotId;
189            Uri uri = ContentUris.withAppendedId(Snapshots.CONTENT_URI, id);
190            return mContentResolver.query(uri, PROJECTION, null, null, null);
191        }
192
193        private InputStream getInputStream(Cursor c) throws FileNotFoundException {
194            String path = c.getString(SNAPSHOT_VIEWSTATE_PATH);
195            if (!TextUtils.isEmpty(path)) {
196                return mContext.openFileInput(path);
197            }
198            byte[] data = c.getBlob(SNAPSHOT_VIEWSTATE);
199            ByteArrayInputStream bis = new ByteArrayInputStream(data);
200            return bis;
201        }
202
203        @Override
204        protected void onPostExecute(Cursor result) {
205            try {
206                if (result.moveToFirst()) {
207                    mTab.mCurrentState.mTitle = result.getString(SNAPSHOT_TITLE);
208                    mTab.mCurrentState.mUrl = result.getString(SNAPSHOT_URL);
209                    byte[] favicon = result.getBlob(SNAPSHOT_FAVICON);
210                    if (favicon != null) {
211                        mTab.mCurrentState.mFavicon = BitmapFactory
212                                .decodeByteArray(favicon, 0, favicon.length);
213                    }
214                    WebViewClassic web = mTab.getWebViewClassic();
215                    if (web != null) {
216                        InputStream ins = getInputStream(result);
217                        GZIPInputStream stream = new GZIPInputStream(ins);
218                        web.loadViewState(stream);
219                    }
220                    mTab.mBackgroundColor = result.getInt(SNAPSHOT_BACKGROUND);
221                    mTab.mDateCreated = result.getLong(SNAPSHOT_DATE_CREATED);
222                    mTab.mWebViewController.onPageFinished(mTab);
223                }
224            } catch (Exception e) {
225                Log.w(LOGTAG, "Failed to load view state, closing tab", e);
226                mTab.mWebViewController.closeTab(mTab);
227            } finally {
228                if (result != null) {
229                    result.close();
230                }
231                mTab.mLoadTask = null;
232            }
233        }
234
235    }
236
237    @Override
238    protected void persistThumbnail() {
239        if (mIsLive) {
240            super.persistThumbnail();
241        }
242    }
243}
244