package com.erakk.lnreader.helper; /** * Created by Greg Kochaniak, http://www.hyperionics.com * License: public domain. */ import android.annotation.SuppressLint; import android.util.Base64; import android.util.Log; import android.webkit.WebResourceResponse; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.Node; import org.w3c.dom.NodeList; import org.w3c.dom.Text; import java.io.ByteArrayInputStream; import java.io.InputStream; import java.util.ArrayList; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; public abstract class WebArchiveReader { private static final String TAG = WebArchiveReader.class.toString(); private Document myDoc = null; private static boolean myLoadingArchive = false; private WebView myWebView = null; private final ArrayList<String> urlList = new ArrayList<String>(); private final ArrayList<Element> urlNodes = new ArrayList<Element>(); private final WebViewClient originalClient; public WebArchiveReader(WebViewClient originalClient) { this.originalClient = originalClient; } protected abstract void onFinished(WebView webView); public boolean readWebArchive(InputStream is) { DocumentBuilderFactory builderFactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = null; myDoc = null; try { builder = builderFactory.newDocumentBuilder(); } catch (ParserConfigurationException e) { e.printStackTrace(); } try { myDoc = builder.parse(is); NodeList nl = myDoc.getElementsByTagName("url"); for (int i = 0; i < nl.getLength(); i++) { Node nd = nl.item(i); if (nd instanceof Element) { Element el = (Element) nd; // siblings of el (url) are: mimeType, textEncoding, frameName, data NodeList nodes = el.getChildNodes(); for (int j = 0; j < nodes.getLength(); j++) { Node node = nodes.item(j); if (node instanceof Text) { String dt = ((Text) node).getData(); byte[] b = Base64.decode(dt, Base64.DEFAULT); dt = new String(b); urlList.add(dt); urlNodes.add((Element) el.getParentNode()); } } } } } catch (Exception e) { Log.e(TAG, "Failed to read web archive", e); myDoc = null; } return myDoc != null; } private byte[] getElBytes(Element el, String childName) { try { Node kid = el.getFirstChild(); while (kid != null) { if (childName.equals(kid.getNodeName())) { Node nn = kid.getFirstChild(); if (nn instanceof Text) { String dt = ((Text) nn).getData(); return Base64.decode(dt, Base64.DEFAULT); } } kid = kid.getNextSibling(); } } catch (Exception e) { e.printStackTrace(); } return null; } public boolean loadToWebView(final WebView v, final String anchorLink, final String historyUrl) { myWebView = v; try { myWebView.setWebViewClient(new WACWebClient()); WebSettings webSettings = myWebView.getSettings(); webSettings.setDefaultTextEncodingName("UTF-8"); myLoadingArchive = true; // Find the first ArchiveResource in myDoc, should be <ArchiveResource> Element ar = (Element) myDoc.getDocumentElement().getFirstChild().getFirstChild(); byte b[] = getElBytes(ar, "data"); // Find out the web page charset encoding String charset = null; String topHtml = new String(b).toLowerCase(); int n1 = topHtml.indexOf("<meta http-equiv=\"content-type\""); if (n1 > -1) { int n2 = topHtml.indexOf('>', n1); if (n2 > -1) { String tag = topHtml.substring(n1, n2); n1 = tag.indexOf("charset"); if (n1 > -1) { tag = tag.substring(n1); n1 = tag.indexOf('='); if (n1 > -1) { tag = tag.substring(n1 + 1); tag = tag.trim(); n1 = tag.indexOf('\"'); if (n1 < 0) n1 = tag.indexOf('\''); if (n1 > -1) { charset = tag.substring(0, n1).trim(); } } } } } if (charset != null) topHtml = new String(b, charset); else { topHtml = new String(b); /* * CharsetMatch match = new CharsetDetector().setText(b).detect(); * if (match != null) * try { * Lt.d("Guessed enc: " + match.getName() + " conf: " + match.getConfidence()); * topHtml = new String(b, match.getName()); * } catch (UnsupportedEncodingException ue) { * topHtml = new String(b); * } */ } String baseUrl = Util.SanitizeBaseUrl(new String(getElBytes(ar, "url"))); if (!Util.isStringNullOrEmpty(anchorLink)) { String[] urlParts = baseUrl.split("#", 2); if (urlParts.length == 2) { baseUrl = urlParts[0] + "#" + anchorLink; } } Log.d(TAG, "Base Url: " + baseUrl); v.loadDataWithBaseURL(baseUrl, topHtml, "text/html", "UTF-8", historyUrl); } catch (Exception e) { Log.e(TAG, "Failed to load web archive", e); myWebView.setWebViewClient(originalClient); return false; } return true; } private class WACWebClient extends WebViewClient { @SuppressLint("NewApi") @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { if (!myLoadingArchive) return null; int n = urlList.indexOf(url); if (n < 0) return null; Element parentEl = urlNodes.get(n); byte[] b = getElBytes(parentEl, "mimeType"); String mimeType = b == null ? "text/html" : new String(b); b = getElBytes(parentEl, "textEncoding"); String encoding = b == null ? "UTF-8" : new String(b); b = getElBytes(parentEl, "data"); return new WebResourceResponse(mimeType, encoding, new ByteArrayInputStream(b)); } @Override public void onPageFinished(WebView view, String url) { // our WACWebClient is no longer needed in view view.setWebViewClient(originalClient); myLoadingArchive = false; onFinished(myWebView); } } }