/******************************************************************************* * Copyright 2013-2015 alladin-IT GmbH * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. ******************************************************************************/ package at.alladin.rmbt.android.test; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import android.content.Context; import android.graphics.Bitmap; import android.net.TrafficStats; import android.os.Handler; import android.os.Process; import android.webkit.WebView; import android.webkit.WebViewClient; import at.alladin.rmbt.android.util.AsyncHtmlStatusCodeRetriever; import at.alladin.rmbt.android.util.AsyncHtmlStatusCodeRetriever.ContentRetrieverListener; import at.alladin.rmbt.client.v2.task.service.WebsiteTestService; /** * * @author lb * */ public class WebsiteTestServiceImpl implements WebsiteTestService { /** * <p> * if set to true the traffic will be recorded using * {@link TrafficStats#getUidRxBytes(int)} and {@link TrafficStats#getUidTxPackets(int)} * </p> * <p> * otherwise this service will call: * {@link TrafficStats#getTotalRxBytes()} and {@link TrafficStats#getTotalTxPackets()} * </p> */ private final static boolean USE_PROCESS_UID_FOR_TRAFFIC_MEASUREMENT = true; private WebView webView; private final Context context; private final AtomicBoolean isRunning = new AtomicBoolean(false); private final AtomicBoolean hasFinished = new AtomicBoolean(false); private final AtomicBoolean hasError = new AtomicBoolean(false); private final AtomicInteger resourceCount = new AtomicInteger(0); private int statusCode = -1; private long duration = -1; private RenderingListener listener; private long trafficRxStart; private long trafficTxStart; private long trafficRxEnd; private long trafficTxEnd; private final Handler handler; private int processUid; public WebsiteTestServiceImpl(final Context context) { this.context = context; this.handler = new Handler(context.getMainLooper()); } /** * * @return */ public WebsiteTestServiceImpl getInstance() { return new WebsiteTestServiceImpl(context); } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#getHash() */ @Override public String getHash() { // TODO Auto-generated method stub return null; } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#getDownloadDuration() */ @Override public long getDownloadDuration() { return duration; } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#run(java.lang.String, long) */ @Override public void run(final String targetUrl, final long timeOut) { handler.post(new Runnable() { @Override public void run() { WebsiteTestServiceImpl.this.processUid = Process.myUid(); //webView.removeAllViews(); if (webView == null) webView = new WebView(context); webView.clearCache(true); final long start = System.nanoTime(); System.out.println("Running WEBSITETASK " + targetUrl); boolean isTrafficServiceSupported = USE_PROCESS_UID_FOR_TRAFFIC_MEASUREMENT ? TrafficStats.getUidRxBytes(processUid) != TrafficStats.UNSUPPORTED : TrafficStats.getTotalRxBytes() != TrafficStats.UNSUPPORTED; if (!isTrafficServiceSupported) { trafficRxStart = -1; trafficTxStart = -1; trafficRxEnd = -1; trafficTxEnd = -1; } else { if (USE_PROCESS_UID_FOR_TRAFFIC_MEASUREMENT) { trafficTxStart = TrafficStats.getUidTxBytes(processUid); trafficRxStart = TrafficStats.getUidRxBytes(processUid); } else { trafficTxStart = TrafficStats.getTotalTxBytes(); trafficRxStart = TrafficStats.getTotalRxBytes(); } } Thread timeoutThread = new Thread(new Runnable() { @Override public void run() { try { System.out.println("WEBSITETASK STARTING TIMEOUT THREAD: " + timeOut + " ms"); Thread.sleep(timeOut); } catch (InterruptedException e) { e.printStackTrace(); Thread.currentThread().interrupt(); // restore interrupt state return; } if (!WebsiteTestServiceImpl.this.hasFinished() && listener != null) { setEndTrafficCounter(); if (listener.onTimeoutReached(WebsiteTestServiceImpl.this)) { System.out.println("WEBSITETESTTASK TIMEOUT"); WebsiteTestServiceImpl.this.handler.post(new Runnable() { @Override public void run() { WebsiteTestServiceImpl.this.webView.stopLoading(); } }); } } } }); timeoutThread.start(); webView.getSettings().setJavaScriptEnabled(true); webView.setWebViewClient(new WebViewClient() { /* @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } */ @Override public void onLoadResource(WebView view, String url) { System.out.println("getting resource: " + url + " progress: " + view.getProgress()); resourceCount.incrementAndGet(); super.onLoadResource(view, url); } @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); WebsiteTestServiceImpl.this.isRunning.set(false); WebsiteTestServiceImpl.this.hasFinished.set(true); WebsiteTestServiceImpl.this.hasError.set(false); WebsiteTestServiceImpl.this.duration = System.nanoTime() - start; if (WebsiteTestServiceImpl.this.trafficRxStart != -1) { setEndTrafficCounter(); } System.out.println("PAGE FINISHED " + targetUrl + " progress: " + view.getProgress() + "%, resources counter: " + resourceCount.get()); if (listener != null) { listener.onRenderFinished(WebsiteTestServiceImpl.this); } } @Override public void onPageStarted(final WebView view, String url, Bitmap favicon) { WebsiteTestServiceImpl.this.isRunning.set(true); WebsiteTestServiceImpl.this.hasFinished.set(false); WebsiteTestServiceImpl.this.hasError.set(false); if (listener != null) { listener.onDownloadStarted(WebsiteTestServiceImpl.this); } System.out.println("PAGE STARTED " + targetUrl); super.onPageStarted(view, url, favicon); } @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) { super.onReceivedError(view, errorCode, description, failingUrl); WebsiteTestServiceImpl.this.isRunning.set(false); WebsiteTestServiceImpl.this.hasFinished.set(true); WebsiteTestServiceImpl.this.hasError.set(true); WebsiteTestServiceImpl.this.duration = System.nanoTime() - start; if (WebsiteTestServiceImpl.this.trafficRxStart != -1) { setEndTrafficCounter(); } if (listener != null) { listener.onError(WebsiteTestServiceImpl.this); } } }); AsyncHtmlStatusCodeRetriever task = new AsyncHtmlStatusCodeRetriever(); task.setContentRetrieverListener(new ContentRetrieverListener() { @Override public void onContentFinished(Integer statusCode) { if (statusCode == null) statusCode = -1; WebsiteTestServiceImpl.this.statusCode = statusCode; if (statusCode >= 0) { //webView.loadDataWithBaseURL(targetUrl, htmlContent, "text/html", "utf-8", null); webView.loadUrl(targetUrl); //webView.loadData(htmlContent, "text/html", "utf-8"); } else { WebsiteTestServiceImpl.this.isRunning.set(false); WebsiteTestServiceImpl.this.hasFinished.set(true); WebsiteTestServiceImpl.this.hasError.set(true); WebsiteTestServiceImpl.this.duration = System.nanoTime() - start; if (WebsiteTestServiceImpl.this.trafficRxStart != -1) { setEndTrafficCounter(); } if (listener != null) { listener.onError(WebsiteTestServiceImpl.this); } } } }); task.execute(targetUrl); //webView.loadUrl(targetUrl); } }); } /** * */ private void setEndTrafficCounter() { if (USE_PROCESS_UID_FOR_TRAFFIC_MEASUREMENT) { this.trafficRxEnd = TrafficStats.getUidRxBytes(processUid); this.trafficTxEnd = TrafficStats.getUidTxBytes(processUid); } else { this.trafficRxEnd = TrafficStats.getTotalRxBytes(); this.trafficTxEnd = TrafficStats.getTotalTxBytes(); } } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#isRunning() */ @Override public boolean isRunning() { final boolean isRunning = this.isRunning.get(); return isRunning; } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#hasFinished() */ @Override public boolean hasFinished() { final boolean hasFinished = this.hasFinished.get(); return hasFinished; } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#setOnRenderingFinishedListener(at.alladin.rmbt.client.v2.task.WebsiteTest.RenderingFinishedListener) */ @Override public void setOnRenderingFinishedListener(RenderingListener listener) { this.listener = listener; } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#hasError() */ @Override public boolean hasError() { return this.hasError.get(); } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#getStatusCode() */ @Override public int getStatusCode() { return statusCode; } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#getTxBytes() */ @Override public long getTxBytes() { return (trafficTxStart != -1 ? trafficTxEnd - trafficTxStart : -1); } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#getRxBytes() */ @Override public long getRxBytes() { return (trafficRxStart != -1 ? trafficRxEnd - trafficRxStart : -1); } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.WebsiteTest#getTotalTrafficBytes() */ @Override public long getTotalTrafficBytes() { return (getRxBytes() != -1 ? getRxBytes() + getTxBytes() : -1); } /* * (non-Javadoc) * @see at.alladin.rmbt.client.v2.task.service.WebsiteTestService#getResourceCount() */ @Override public int getResourceCount() { return resourceCount.get(); } }