org.kotemaru.android.sample.webview.XMLHttpRequestXS.java Source code

Java tutorial

Introduction

Here is the source code for org.kotemaru.android.sample.webview.XMLHttpRequestXS.java

Source

/*******************************************************************************
 * Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
 * Copyright (c) 2014- kotemaru@kotemaru.org
 ******************************************************************************/
package org.kotemaru.android.sample.webview;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.util.Locale;

import javax.xml.parsers.ParserConfigurationException;

import org.apache.http.Header;
import org.apache.http.HeaderElement;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.StringEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.message.BasicHeader;
import org.apache.http.protocol.HTTP;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import android.os.AsyncTask;
import android.util.Log;
import android.webkit.CookieManager;
import android.webkit.JavascriptInterface;

/**
 * XMLHttpRequest?
 * - ???XMLHttpRequest???????
 * - XMLHttpRequstXS.js?????
 * - ?API Level 17 ??OS?????????
 */
public class XMLHttpRequestXS {
    private static final String TAG = "XMLHttpRequest";

    private static final String GET = "GET";
    private static final String POST = "POST";
    private static final String CONTENT_TYPE = "Content-type";
    private static final String TRIGGER_SCRIPT = "javascript:XMLHttpRequestXS._staticNativeCallback";

    private static final String MIME_TEXT = "text/plain";
    private static final String MIME_XML = "text/xml";

    private static final int UNSENT = 0; // open() ?????????
    private static final int OPENED = 1; // send() ?????????
    private static final int HEADERS_RECEIVED = 2; // send()
    // ????????
    private static final int LOADING = 3; // responseText
    // ???????
    private static final int DONE = 4; // ????
    private static final int ERROR = 5; // ????????

    private XMLHttpRequestXSFactory _factory;

    private HttpClient _client;
    private HttpRequestBase _request;
    private HttpResponse _response;
    private boolean _isAsync;
    private int _readyState;
    private String _triggerScript;

    private int _responseCode;
    private String _responseText = null;
    private Document _responseXML = null;
    private String _overrideMimeType = null;
    private String _responseType = null;
    private String _mimeType = null;
    private String _charset = null;

    public XMLHttpRequestXS(XMLHttpRequestXSFactory factory) {
        _factory = factory;
        _client = new DefaultHttpClient();
        _triggerScript = TRIGGER_SCRIPT + "(" + identityHashCode() + ")";
        setReadyState(UNSENT);
    }

    @JavascriptInterface
    public int identityHashCode() {
        return System.identityHashCode(this);
    }

    @JavascriptInterface
    public void open(String type, String url) throws Exception {
        open(type, url, false);
    }

    @JavascriptInterface
    public String open(String type, String url, boolean isAsync) throws Exception {
        Log.d(TAG, "open:" + type + " " + url);
        try {
            _isAsync = isAsync;
            if (GET.equalsIgnoreCase(type)) {
                _request = new HttpGet(url);
            } else {
                _request = new HttpPost(url);
            }
            _factory.checkDomain(_request.getURI());

            String cookie = CookieManager.getInstance().getCookie(url);
            _request.setHeader("Cookie", cookie);
            setReadyState(OPENED);
            return null;
        } catch (Throwable t) {
            Log.e(TAG, t.getMessage(), t);
            setReadyState(ERROR);
            return t.getMessage();
        }
    }

    @JavascriptInterface
    public void abort() throws Exception {
        if (_request != null)
            _request.abort();
    }

    @JavascriptInterface
    public void send(String body) throws Throwable {
        Log.d(TAG, "send:");
        if (_isAsync) {
            new RequestAsyncTask().execute(body);
        } else {
            doRequest(body);
            setReadyState(HEADERS_RECEIVED);
            setReadyState(LOADING);
            doResponse();
            setReadyState(DONE);
        }
    }

    private class RequestAsyncTask extends AsyncTask<String, Integer, Throwable> {
        @Override
        protected Throwable doInBackground(String... args) {
            try {
                doRequest(args[0]);
                publishProgress(HEADERS_RECEIVED);
                publishProgress(LOADING);
                doResponse();
                publishProgress(DONE);
            } catch (Throwable t) {
                return t;
            }
            return null;
        }

        @Override
        protected void onProgressUpdate(Integer... values) {
            setReadyState(values[0]);
            _factory.runScript(_triggerScript);
        }

        @Override
        protected void onPostExecute(Throwable error) {
            if (error != null) {
                setReadyState(ERROR);
                _factory.runScript(_triggerScript);
            }
        }
    }

    private void doRequest(String body) throws Throwable {
        try {
            if (POST.equals(_request.getMethod())) {
                HttpPost httpPost = (HttpPost) _request;
                Header header = httpPost.getFirstHeader(CONTENT_TYPE);
                String ctype = header != null ? header.getValue() : "plain/text; charset=" + HTTP.UTF_8;
                httpPost.setEntity(new StringEntity(body, ctype));
            }
            _response = _client.execute(_request);
            _responseCode = _response.getStatusLine().getStatusCode();

            CookieManager cookieManager = CookieManager.getInstance();
            Header[] cookies = _response.getHeaders("Set-Cookie");
            for (int i = 0; i < cookies.length; i++) {
                cookieManager.setCookie(_request.getURI().toString(), cookies[i].getValue());
            }

            if (Log.isLoggable(TAG, Log.DEBUG))
                debugLog();
        } catch (Throwable t) {
            Log.e(TAG, t.getMessage(), t);
            _request.abort();
            throw t;
        }
    }

    private void doResponse() throws Throwable {
        try {
            HttpEntity entity = _response.getEntity();
            Header contentType = getOverrideContentType();
            if (contentType == null) {
                contentType = entity.getContentType();
                if (contentType == null) {
                    contentType = new BasicHeader(CONTENT_TYPE, MIME_TEXT);
                }
            }
            HeaderElement[] elems = contentType.getElements();
            _mimeType = (elems.length > 0) ? elems[0].getName().toLowerCase(Locale.US) : "";
            _charset = HTTP.UTF_8; // default
            for (int i = 1; i < elems.length; i++) {
                if ("charset".equals(elems[i].getName().toLowerCase(Locale.US))) {
                    _charset = elems[i].getValue();
                }
            }
            _responseText = getEntityString(entity, _charset);
        } catch (Throwable t) {
            Log.e(TAG, t.getMessage(), t);
            _responseCode = 500;
            throw t;
        } finally {
            _request.abort();
        }
    }

    private Header getOverrideContentType() {
        if (_overrideMimeType != null) {
            return new BasicHeader(CONTENT_TYPE, _overrideMimeType);
        }
        if ("document".equals(_responseType)) {
            return new BasicHeader(CONTENT_TYPE, MIME_XML);
        }
        return null;
    }

    private void setReadyState(int state) {
        _readyState = state;
    }

    @JavascriptInterface
    public String getResponseHeader(String name) {
        Header[] headers = _response.getHeaders(name);
        if (headers == null || headers.length == 0)
            return null;
        if (headers.length == 1)
            return headers[0].getValue();

        StringBuilder sbuf = new StringBuilder();
        for (int i = 0; i < headers.length; i++) {
            if (i > 0)
                sbuf.append(',');
            sbuf.append(headers[i].getValue());
        }
        return sbuf.toString();
    }

    @JavascriptInterface
    public String getAllResponseHeaders() {
        StringBuilder sbuf = new StringBuilder();
        Header[] headers = _response.getAllHeaders();
        for (int i = 0; i < headers.length; i++) {
            sbuf.append(headers[i].getName()).append(": ").append(headers[i].getValue());
        }
        return sbuf.toString();
    }

    private String getEntityString(HttpEntity entity, String charset) throws IllegalStateException, IOException {
        InputStream in = entity.getContent();
        try {
            Reader reader = new InputStreamReader(in, charset);
            StringBuilder sbuf = new StringBuilder();
            char[] buff = new char[1500];
            int n = 0;
            while ((n = reader.read(buff)) >= 0) {
                sbuf.append(buff, 0, n);
            }
            return sbuf.toString();
        } finally {
            in.close();
        }
    }

    @JavascriptInterface
    public int getReadyState() {
        return _readyState;
    }

    @JavascriptInterface
    public int getStatus() {
        return _responseCode;
    }

    @JavascriptInterface
    public String getStatusText() {
        if (_response == null)
            return null;
        return _response.getStatusLine().getReasonPhrase();
    }

    @JavascriptInterface
    public String getResponseText() throws IllegalStateException, IOException {
        return _responseText;
    }

    @JavascriptInterface
    public Document getResponseXML()
            throws IllegalStateException, SAXException, IOException, ParserConfigurationException {
        return _responseXML;
    }

    @JavascriptInterface
    public void setRequestHeader(String name, String value) {
        _request.setHeader(name, value);
    }

    @JavascriptInterface
    public void overrideMimeType(String mimeType) {
        this._overrideMimeType = mimeType;
    }

    @JavascriptInterface
    public String getResponseType() {
        return _responseType;
    }

    @JavascriptInterface
    public void setResponseType(String type) {
        this._responseType = type;
    }

    @JavascriptInterface
    public String getMimeType() {
        return _mimeType;
    }

    @JavascriptInterface
    public String getCharset() {
        return _charset;
    }

    private void debugLog() throws Exception {
        String msg = "XREQ request detail.\n";
        msg += ">> " + _request.getMethod() + " " + _request.getURI() + "\n";
        Header[] reqhs = _request.getAllHeaders();
        for (int i = 0; i < reqhs.length; i++) {
            msg += ">> " + reqhs[i] + "\n";
        }

        msg += "\n<< " + _response.getStatusLine() + "\n";
        Header[] reshs = _response.getAllHeaders();
        for (int i = 0; i < reshs.length; i++) {
            msg += "<< " + reshs[i] + "\n";
        }

        Log.d(TAG, msg);
    }

}