Home | History | Annotate | Download | only in tilebenchmark
      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 
     49     public ProfiledWebView(Context context) {
     50         super(context);
     51     }
     52 
     53     public ProfiledWebView(Context context, AttributeSet attrs) {
     54         super(context, attrs);
     55     }
     56 
     57     public ProfiledWebView(Context context, AttributeSet attrs, int defStyle) {
     58         super(context, attrs, defStyle);
     59     }
     60 
     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 */
     70         JavaScriptInterface(Context c) {
     71             mContext = c;
     72         }
     73 
     74         public void animationComplete() {
     75             mAnimationTime = System.currentTimeMillis();
     76         }
     77     }
     78 
     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 
     92     public void setUseMinimalMemory(boolean minimal) {
     93         WebSettingsClassic settings = getWebViewClassic().getSettings();
     94         settings.setProperty("use_minimal_memory", minimal ? "true" : "false");
     95     }
     96 
     97     public void onPageFinished() {
     98         mLoadTime = System.currentTimeMillis();
     99     }
    100 
    101     @Override
    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      */
    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
    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 
    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 
    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      */
    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
    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 
    245     public void setAutoScrollSpeed(int speedInt) {
    246         mSpeed = speedInt;
    247     }
    248 
    249     public WebViewClassic getWebViewClassic() {
    250         return WebViewClassic.fromWebView(this);
    251     }
    252 }
    253