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