1 /* 2 * Copyright (C) 2011 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 17 package com.test.tilebenchmark; 18 19 import android.content.Context; 20 import android.os.CountDownTimer; 21 import android.util.AttributeSet; 22 import android.util.Log; 23 import android.webkit.WebSettingsClassic; 24 import android.webkit.WebView; 25 import android.webkit.WebViewClassic; 26 27 import java.util.ArrayList; 28 29 import com.test.tilebenchmark.ProfileActivity.ProfileCallback; 30 import com.test.tilebenchmark.RunData.TileData; 31 32 public class ProfiledWebView extends WebView implements WebViewClassic.PageSwapDelegate { 33 private static final String LOGTAG = "ProfiledWebView"; 34 35 private int mSpeed; 36 37 private boolean mIsTesting = false; 38 private boolean mIsScrolling = false; 39 private ProfileCallback mCallback; 40 private long mContentInvalMillis; 41 private static final int LOAD_STALL_MILLIS = 2000; // nr of millis after load, 42 // before test is forced 43 44 // ignore anim end events until this many millis after load 45 private static final long ANIM_SAFETY_THRESHOLD = 200; 46 private long mLoadTime; 47 private long mAnimationTime; 48 ProfiledWebView(Context context)49 public ProfiledWebView(Context context) { 50 super(context); 51 } 52 ProfiledWebView(Context context, AttributeSet attrs)53 public ProfiledWebView(Context context, AttributeSet attrs) { 54 super(context, attrs); 55 } 56 ProfiledWebView(Context context, AttributeSet attrs, int defStyle)57 public ProfiledWebView(Context context, AttributeSet attrs, int defStyle) { 58 super(context, attrs, defStyle); 59 } 60 ProfiledWebView(Context context, AttributeSet attrs, int defStyle, boolean privateBrowsing)61 public ProfiledWebView(Context context, AttributeSet attrs, int defStyle, 62 boolean privateBrowsing) { 63 super(context, attrs, defStyle, privateBrowsing); 64 } 65 66 private class JavaScriptInterface { 67 Context mContext; 68 69 /** Instantiate the interface and set the context */ JavaScriptInterface(Context c)70 JavaScriptInterface(Context c) { 71 mContext = c; 72 } 73 animationComplete()74 public void animationComplete() { 75 mAnimationTime = System.currentTimeMillis(); 76 } 77 } 78 init(Context c)79 public void init(Context c) { 80 WebSettingsClassic settings = getWebViewClassic().getSettings(); 81 settings.setJavaScriptEnabled(true); 82 settings.setSupportZoom(true); 83 settings.setEnableSmoothTransition(true); 84 settings.setBuiltInZoomControls(true); 85 settings.setLoadWithOverviewMode(true); 86 settings.setProperty("use_minimal_memory", "false"); // prefetch tiles, as browser does 87 addJavascriptInterface(new JavaScriptInterface(c), "Android"); 88 mAnimationTime = 0; 89 mLoadTime = 0; 90 } 91 setUseMinimalMemory(boolean minimal)92 public void setUseMinimalMemory(boolean minimal) { 93 WebSettingsClassic settings = getWebViewClassic().getSettings(); 94 settings.setProperty("use_minimal_memory", minimal ? "true" : "false"); 95 } 96 onPageFinished()97 public void onPageFinished() { 98 mLoadTime = System.currentTimeMillis(); 99 } 100 101 @Override onDraw(android.graphics.Canvas canvas)102 protected void onDraw(android.graphics.Canvas canvas) { 103 if (mIsTesting && mIsScrolling) { 104 if (canScrollVertically(1)) { 105 scrollBy(0, mSpeed); 106 } else { 107 stopScrollTest(); 108 mIsScrolling = false; 109 } 110 } 111 super.onDraw(canvas); 112 } 113 114 /* 115 * Called once the page is loaded to start scrolling for evaluating tiles. 116 * If autoScrolling isn't set, stop must be called manually. Before 117 * scrolling, invalidate all content and redraw it, measuring time taken. 118 */ startScrollTest(ProfileCallback callback, boolean autoScrolling)119 public void startScrollTest(ProfileCallback callback, boolean autoScrolling) { 120 mCallback = callback; 121 mIsTesting = false; 122 mIsScrolling = false; 123 WebSettingsClassic settings = getWebViewClassic().getSettings(); 124 settings.setProperty("tree_updates", "0"); 125 126 127 if (autoScrolling) { 128 // after a while, force it to start even if the pages haven't swapped 129 new CountDownTimer(LOAD_STALL_MILLIS, LOAD_STALL_MILLIS) { 130 @Override 131 public void onTick(long millisUntilFinished) { 132 } 133 134 @Override 135 public void onFinish() { 136 // invalidate all content, and kick off redraw 137 Log.d("ProfiledWebView", 138 "kicking off test with callback registration, and tile discard..."); 139 getWebViewClassic().discardAllTextures(); 140 invalidate(); 141 mIsScrolling = true; 142 mContentInvalMillis = System.currentTimeMillis(); 143 } 144 }.start(); 145 } else { 146 mIsTesting = true; 147 getWebViewClassic().tileProfilingStart(); 148 } 149 } 150 151 /* 152 * Called after the manual contentInvalidateAll, after the tiles have all 153 * been redrawn. 154 * From PageSwapDelegate. 155 */ 156 @Override onPageSwapOccurred(boolean startAnim)157 public void onPageSwapOccurred(boolean startAnim) { 158 if (!mIsTesting && mIsScrolling) { 159 // kick off testing 160 mContentInvalMillis = System.currentTimeMillis() - mContentInvalMillis; 161 Log.d("ProfiledWebView", "REDRAW TOOK " + mContentInvalMillis + "millis"); 162 mIsTesting = true; 163 invalidate(); // ensure a redraw so that auto-scrolling can occur 164 getWebViewClassic().tileProfilingStart(); 165 } 166 } 167 animFramerate()168 private double animFramerate() { 169 WebSettingsClassic settings = getWebViewClassic().getSettings(); 170 String updatesString = settings.getProperty("tree_updates"); 171 int updates = (updatesString == null) ? -1 : Integer.parseInt(updatesString); 172 173 long animationTime; 174 if (mAnimationTime == 0 || mAnimationTime - mLoadTime < ANIM_SAFETY_THRESHOLD) { 175 animationTime = System.currentTimeMillis() - mLoadTime; 176 } else { 177 animationTime = mAnimationTime - mLoadTime; 178 } 179 180 return updates * 1000.0 / animationTime; 181 } 182 setDoubleBuffering(boolean useDoubleBuffering)183 public void setDoubleBuffering(boolean useDoubleBuffering) { 184 WebSettingsClassic settings = getWebViewClassic().getSettings(); 185 settings.setProperty("use_double_buffering", useDoubleBuffering ? "true" : "false"); 186 } 187 188 /* 189 * Called once the page has stopped scrolling 190 */ stopScrollTest()191 public void stopScrollTest() { 192 getWebViewClassic().tileProfilingStop(); 193 mIsTesting = false; 194 195 if (mCallback == null) { 196 getWebViewClassic().tileProfilingClear(); 197 return; 198 } 199 200 RunData data = new RunData(getWebViewClassic().tileProfilingNumFrames()); 201 // record the time spent (before scrolling) rendering the page 202 data.singleStats.put(getResources().getString(R.string.render_millis), 203 (double)mContentInvalMillis); 204 205 // record framerate 206 double framerate = animFramerate(); 207 Log.d(LOGTAG, "anim framerate was "+framerate); 208 data.singleStats.put(getResources().getString(R.string.animation_framerate), 209 framerate); 210 211 for (int frame = 0; frame < data.frames.length; frame++) { 212 data.frames[frame] = new TileData[ 213 getWebViewClassic().tileProfilingNumTilesInFrame(frame)]; 214 for (int tile = 0; tile < data.frames[frame].length; tile++) { 215 int left = getWebViewClassic().tileProfilingGetInt(frame, tile, "left"); 216 int top = getWebViewClassic().tileProfilingGetInt(frame, tile, "top"); 217 int right = getWebViewClassic().tileProfilingGetInt(frame, tile, "right"); 218 int bottom = getWebViewClassic().tileProfilingGetInt(frame, tile, "bottom"); 219 220 boolean isReady = getWebViewClassic().tileProfilingGetInt( 221 frame, tile, "isReady") == 1; 222 int level = getWebViewClassic().tileProfilingGetInt(frame, tile, "level"); 223 224 float scale = getWebViewClassic().tileProfilingGetFloat(frame, tile, "scale"); 225 226 data.frames[frame][tile] = data.new TileData(left, top, right, bottom, 227 isReady, level, scale); 228 } 229 } 230 getWebViewClassic().tileProfilingClear(); 231 232 mCallback.profileCallback(data); 233 } 234 235 @Override loadUrl(String url)236 public void loadUrl(String url) { 237 mAnimationTime = 0; 238 mLoadTime = 0; 239 if (!url.startsWith("http://") && !url.startsWith("file://")) { 240 url = "http://" + url; 241 } 242 super.loadUrl(url); 243 } 244 setAutoScrollSpeed(int speedInt)245 public void setAutoScrollSpeed(int speedInt) { 246 mSpeed = speedInt; 247 } 248 getWebViewClassic()249 public WebViewClassic getWebViewClassic() { 250 return WebViewClassic.fromWebView(this); 251 } 252 } 253