1/*
2 * Copyright (C) 2009 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 */
16
17package com.android.browserpowertest;
18
19import android.app.Activity;
20import android.app.ActivityThread;
21import android.graphics.Bitmap;
22import android.os.Bundle;
23import android.os.Handler;
24import android.os.Looper;
25import android.os.Message;
26import android.util.Log;
27import android.view.ViewGroup;
28import android.webkit.WebChromeClient;
29import android.webkit.WebView;
30import android.webkit.WebViewClient;
31import android.webkit.WebSettings.LayoutAlgorithm;
32import android.widget.LinearLayout;
33import android.widget.LinearLayout.LayoutParams;
34
35public class PowerTestActivity extends Activity {
36
37    public static final String LOGTAG = "PowerTestActivity";
38    public static final String PARAM_URL = "URL";
39    public static final String PARAM_TIMEOUT = "Timeout";
40    public static final int RESULT_TIMEOUT = 0xDEAD;
41    public static final int MSG_TIMEOUT = 0xC001;
42    public static final int MSG_NAVIGATE = 0xC002;
43    public static final String MSG_NAV_URL = "url";
44    public static final String MSG_NAV_LOGTIME = "logtime";
45
46    private WebView webView;
47    private SimpleWebViewClient webViewClient;
48    private SimpleChromeClient chromeClient;
49    private Handler handler;
50    private boolean timeoutFlag;
51    private boolean logTime;
52    private boolean pageDone;
53    private Object pageDoneLock;
54    private int pageStartCount;
55    private int manualDelay;
56    private long startTime;
57    private long pageLoadTime;
58    private PageDoneRunner pageDoneRunner = new PageDoneRunner();
59
60    public PowerTestActivity() {
61    }
62
63    @Override
64    protected void onCreate(Bundle savedInstanceState) {
65        super.onCreate(savedInstanceState);
66
67        Log.v(LOGTAG, "onCreate, inst=" + Integer.toHexString(hashCode()));
68
69        LinearLayout contentView = new LinearLayout(this);
70        contentView.setOrientation(LinearLayout.VERTICAL);
71        setContentView(contentView);
72        setTitle("Idle");
73
74        webView = new WebView(this);
75        webView.getSettings().setJavaScriptEnabled(true);
76        webView.getSettings().setJavaScriptCanOpenWindowsAutomatically(false);
77        webView.getSettings().setLayoutAlgorithm(LayoutAlgorithm.NORMAL);
78
79        webViewClient = new SimpleWebViewClient();
80        chromeClient = new SimpleChromeClient();
81        webView.setWebViewClient(webViewClient);
82        webView.setWebChromeClient(chromeClient);
83
84        contentView.addView(webView, new LayoutParams(
85                ViewGroup.LayoutParams.MATCH_PARENT,
86                ViewGroup.LayoutParams.MATCH_PARENT, 0.0f));
87
88        handler = new Handler() {
89            @Override
90            public void handleMessage(Message msg) {
91                switch (msg.what) {
92                    case MSG_TIMEOUT:
93                        handleTimeout();
94                        return;
95                    case MSG_NAVIGATE:
96                        manualDelay = msg.arg2;
97                        navigate(msg.getData().getString(MSG_NAV_URL), msg.arg1);
98                        logTime = msg.getData().getBoolean(MSG_NAV_LOGTIME);
99                        return;
100                }
101            }
102        };
103
104        pageDoneLock = new Object();
105    }
106
107    public void reset() {
108        synchronized (pageDoneLock) {
109            pageDone = false;
110        }
111        timeoutFlag = false;
112        pageStartCount = 0;
113        chromeClient.resetJsTimeout();
114    }
115
116    private void navigate(String url, int timeout) {
117        if(url == null) {
118            Log.v(LOGTAG, "URL is null, cancelling...");
119            finish();
120        }
121        webView.stopLoading();
122        if(logTime) {
123            webView.clearCache(true);
124        }
125        startTime = System.currentTimeMillis();
126        Log.v(LOGTAG, "Navigating to URL: " + url);
127        webView.loadUrl(url);
128
129        if(timeout != 0) {
130            //set a timer with specified timeout (in ms)
131            handler.sendMessageDelayed(handler.obtainMessage(MSG_TIMEOUT),
132                    timeout);
133        }
134    }
135
136    @Override
137    protected void onDestroy() {
138        super.onDestroy();
139        Log.v(LOGTAG, "onDestroy, inst=" + Integer.toHexString(hashCode()));
140        webView.clearCache(true);
141        webView.destroy();
142    }
143
144    private boolean isPageDone() {
145        synchronized (pageDoneLock) {
146            return pageDone;
147        }
148    }
149
150    private void setPageDone(boolean pageDone) {
151        synchronized (pageDoneLock) {
152            this.pageDone = pageDone;
153            pageDoneLock.notifyAll();
154        }
155    }
156
157    private void handleTimeout() {
158        int progress = webView.getProgress();
159        webView.stopLoading();
160        Log.v(LOGTAG, "Page timeout triggered, progress = " + progress);
161        timeoutFlag = true;
162        handler.postDelayed(pageDoneRunner, manualDelay);
163    }
164
165    public boolean waitUntilDone() {
166        validateNotAppThread();
167        synchronized (pageDoneLock) {
168            while(!isPageDone()) {
169                try {
170                    pageDoneLock.wait();
171                } catch (InterruptedException ie) {
172                    //no-op
173                }
174            }
175        }
176        return timeoutFlag;
177    }
178
179    public Handler getHandler() {
180        return handler;
181    }
182
183    private final void validateNotAppThread() {
184        if (Looper.myLooper() == Looper.getMainLooper()) {
185            throw new RuntimeException(
186                "This method can not be called from the main application thread");
187        }
188    }
189
190    public long getPageLoadTime() {
191        return pageLoadTime;
192    }
193
194    public boolean getPageError() {
195        return webViewClient.getPageErrorFlag();
196    }
197
198    class SimpleWebViewClient extends WebViewClient {
199
200        private boolean pageErrorFlag = false;
201
202        @Override
203        public void onReceivedError(WebView view, int errorCode, String description,
204                String failingUrl) {
205            pageErrorFlag = true;
206            Log.v(LOGTAG, "WebCore error: code=" + errorCode
207                    + ", description=" + description
208                    + ", url=" + failingUrl);
209        }
210
211        @Override
212        public void onPageStarted(WebView view, String url, Bitmap favicon) {
213            pageStartCount++;
214            Log.v(LOGTAG, "onPageStarted: " + url);
215        }
216
217        @Override
218        public void onPageFinished(WebView view, String url) {
219            Log.v(LOGTAG, "onPageFinished: " + url);
220            // let handleTimeout take care of finishing the page
221            if(!timeoutFlag)
222                handler.postDelayed(new WebViewStatusChecker(), 500);
223        }
224
225        // return true if the URL is not available or the page is down
226        public boolean getPageErrorFlag() {
227            return pageErrorFlag;
228        }
229    }
230
231    class SimpleChromeClient extends WebChromeClient {
232
233        private int timeoutCounter = 0;
234
235        public void resetJsTimeout() {
236            timeoutCounter = 0;
237        }
238
239        @Override
240        public void onReceivedTitle(WebView view, String title) {
241            PowerTestActivity.this.setTitle(title);
242        }
243    }
244
245    class WebViewStatusChecker implements Runnable {
246
247        private int initialStartCount;
248
249        public WebViewStatusChecker() {
250            initialStartCount = pageStartCount;
251        }
252
253        public void run() {
254            if (initialStartCount == pageStartCount && !isPageDone()) {
255                handler.removeMessages(MSG_TIMEOUT);
256                webView.stopLoading();
257                handler.postDelayed(pageDoneRunner, manualDelay);
258            }
259        }
260    }
261
262    class PageDoneRunner implements Runnable {
263
264        public void run() {
265            Log.v(LOGTAG, "Finishing URL: " + webView.getUrl());
266            pageLoadTime = System.currentTimeMillis() - startTime;
267            setPageDone(true);
268        }
269    }
270}
271