19066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/*
29066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
39066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
49066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
59066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * you may not use this file except in compliance with the License.
69066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * You may obtain a copy of the License at
79066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
89066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
99066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
139066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * See the License for the specific language governing permissions and
149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * limitations under the License.
159066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectpackage android.webkit;
189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
198af3cfc2a45334bfe936fcfc79c6e1cab06e104bPatrick Scottimport android.content.Context;
209066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.http.EventHandler;
219066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.net.http.Headers;
229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Handler;
239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport android.os.Message;
249066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
259066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.IOException;
269066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Projectimport java.io.InputStream;
279066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project/**
299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * This abstract class is used for all content loaders that rely on streaming
309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * content into the rendering engine loading framework.
319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * The class implements a state machine to load the content into the frame in
339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * a similar manor to the way content arrives from the network. The class uses
349066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * messages to move from one state to the next, which enables async. loading of
359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * the streamed content.
369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * Classes that inherit from this class must implement two methods, the first
389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * method is used to setup the InputStream and notify the loading framework if
399066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * it can load it's content. The other method allows the derived class to add
409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * additional HTTP headers to the response.
419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * By default, content loaded with a StreamLoader is marked with a HTTP header
439066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project * that indicates the content should not be cached.
449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project *
459066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project */
46ac75f56600691d318d40301204baaf840c9586f2Grace Klobaabstract class StreamLoader implements Handler.Callback {
479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_STATUS = 100;  // Send status to loader
499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_HEADERS = 101; // Send headers to loader
509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_DATA = 102;  // Send data to loader
519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private static final int MSG_END = 103;  // Send endData to loader
529066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
538af3cfc2a45334bfe936fcfc79c6e1cab06e104bPatrick Scott    protected final Context mContext;
54ac75f56600691d318d40301204baaf840c9586f2Grace Kloba    protected final LoadListener mLoadListener; // loader class
559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected InputStream mDataStream; // stream to read data from
569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected long mContentLength; // content length of data
579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private byte [] mData; // buffer to pass data to loader with.
589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
59ac75f56600691d318d40301204baaf840c9586f2Grace Kloba    // Handler which will be initialized in the thread where load() is called.
60ac75f56600691d318d40301204baaf840c9586f2Grace Kloba    private Handler mHandler;
61ac75f56600691d318d40301204baaf840c9586f2Grace Kloba
629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Constructor. Although this class calls the LoadListener, it only calls
649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * the EventHandler Interface methods. LoadListener concrete class is used
659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * to avoid the penality of calling an interface.
669066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param loadlistener The LoadListener to call with the data.
689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    StreamLoader(LoadListener loadlistener) {
70ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        mLoadListener = loadlistener;
718af3cfc2a45334bfe936fcfc79c6e1cab06e104bPatrick Scott        mContext = loadlistener.getContext();
729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method is called when the derived class should setup mDataStream,
76ac75f56600691d318d40301204baaf840c9586f2Grace Kloba     * and call mLoadListener.status() to indicate that the load can occur. If it
779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * fails to setup, it should still call status() with the error code.
789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return true if stream was successfully setup
809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
819066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    protected abstract boolean setupStreamAndSendStatus();
829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * This method is called when the headers are about to be sent to the
859066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * load framework. The derived class has the opportunity to add addition
869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * headers.
879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *
889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @param headers Map of HTTP headers that will be sent to the loader.
899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    abstract protected void buildHeaders(Headers headers);
919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
92ac75f56600691d318d40301204baaf840c9586f2Grace Kloba    /**
939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Calling this method starts the load of the content for this StreamLoader.
94ac75f56600691d318d40301204baaf840c9586f2Grace Kloba     * This method simply creates a Handler in the current thread and posts a
95ac75f56600691d318d40301204baaf840c9586f2Grace Kloba     * message to send the status and returns immediately.
969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
97ac75f56600691d318d40301204baaf840c9586f2Grace Kloba    final void load() {
98ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        synchronized (this) {
99ac75f56600691d318d40301204baaf840c9586f2Grace Kloba            if (mHandler == null) {
100ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                mHandler = new Handler(this);
101ac75f56600691d318d40301204baaf840c9586f2Grace Kloba            }
102ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        }
103ac75f56600691d318d40301204baaf840c9586f2Grace Kloba
104ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        if (!mLoadListener.isSynchronous()) {
105ac75f56600691d318d40301204baaf840c9586f2Grace Kloba            mHandler.sendEmptyMessage(MSG_STATUS);
1069066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        } else {
1079066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            // Load the stream synchronously.
1089066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            if (setupStreamAndSendStatus()) {
1099066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // We were able to open the stream, create the array
1109066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // to pass data to the loader
1119066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mData = new byte[8192];
1129066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendHeaders();
113ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                while (!sendData() && !mLoadListener.cancelled());
1149066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                closeStreamAndSendEndData();
115ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                mLoadListener.loadSynchronousMessages();
1169066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1179066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1189066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1199066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
120ac75f56600691d318d40301204baaf840c9586f2Grace Kloba    public boolean handleMessage(Message msg) {
121ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        if (mLoadListener.isSynchronous()) {
1229066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            throw new AssertionError();
1239066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
124ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        if (mLoadListener.cancelled()) {
125e82dc4205285a40d6c2227e362c30ce24c96a552Patrick Scott            closeStreamAndSendEndData();
126ac75f56600691d318d40301204baaf840c9586f2Grace Kloba            return true;
127e82dc4205285a40d6c2227e362c30ce24c96a552Patrick Scott        }
1289066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        switch(msg.what) {
1299066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MSG_STATUS:
1309066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (setupStreamAndSendStatus()) {
1319066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // We were able to open the stream, create the array
1329066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    // to pass data to the loader
1339066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    mData = new byte[8192];
134ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                    mHandler.sendEmptyMessage(MSG_HEADERS);
1359066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1369066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
1379066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MSG_HEADERS:
1389066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                sendHeaders();
139ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                mHandler.sendEmptyMessage(MSG_DATA);
1409066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
1419066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MSG_DATA:
1429066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (sendData()) {
143ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                    mHandler.sendEmptyMessage(MSG_END);
1449066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                } else {
145ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                    mHandler.sendEmptyMessage(MSG_DATA);
1469066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1479066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
1489066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            case MSG_END:
1499066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                closeStreamAndSendEndData();
1509066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                break;
1519066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            default:
152ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                return false;
1539066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
154ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        return true;
1559066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1569066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1579066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1589066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Construct the headers and pass them to the EventHandler.
1599066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1609066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void sendHeaders() {
1619066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        Headers headers = new Headers();
1629066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mContentLength > 0) {
1639066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            headers.setContentLength(mContentLength);
1649066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1659066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        buildHeaders(headers);
166ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        mLoadListener.headers(headers);
1679066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1689066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1699066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1709066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Read data from the stream and pass it to the EventHandler.
1719066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * If an error occurs reading the stream, then an error is sent to the
1729066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * EventHandler, and moves onto the next state - end of data.
1739066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * @return True if all the data has been read. False if sendData should be
1749066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     *         called again.
1759066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1769066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private boolean sendData() {
1779066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDataStream != null) {
1789066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1799066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                int amount = mDataStream.read(mData);
1809066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                if (amount > 0) {
181ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                    mLoadListener.data(mData, amount);
1829066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                    return false;
1839066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                }
1849066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException ex) {
185ac75f56600691d318d40301204baaf840c9586f2Grace Kloba                mLoadListener.error(EventHandler.FILE_ERROR, ex.getMessage());
1869066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
1879066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
1889066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        return true;
1899066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
1909066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project
1919066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    /**
1929066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     * Close the stream and inform the EventHandler that load is complete.
1939066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project     */
1949066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    private void closeStreamAndSendEndData() {
1959066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        if (mDataStream != null) {
1969066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            try {
1979066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                mDataStream.close();
1989066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            } catch (IOException ex) {
1999066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project                // ignore.
2009066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project            }
2019066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project        }
202ac75f56600691d318d40301204baaf840c9586f2Grace Kloba        mLoadListener.endData();
2039066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project    }
2049066cfe9886ac131c34d59ed0e2d287b0e3c0087The Android Open Source Project}
205