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.app.Activity; 20 import android.content.Intent; 21 import android.content.Context; 22 import android.graphics.Bitmap; 23 import android.os.AsyncTask; 24 import android.os.Bundle; 25 import android.os.CountDownTimer; 26 import android.util.Log; 27 import android.util.Pair; 28 import android.view.KeyEvent; 29 import android.view.View; 30 import android.view.View.OnClickListener; 31 import android.webkit.WebView; 32 import android.webkit.WebViewClient; 33 import android.widget.AdapterView; 34 import android.widget.AdapterView.OnItemSelectedListener; 35 import android.widget.ArrayAdapter; 36 import android.widget.Button; 37 import android.widget.EditText; 38 import android.widget.Spinner; 39 import android.widget.TextView; 40 import android.widget.TextView.OnEditorActionListener; 41 import android.widget.ToggleButton; 42 43 import java.io.FileOutputStream; 44 import java.io.IOException; 45 import java.io.ObjectOutputStream; 46 47 /** 48 * Interface for profiling the webview's scrolling, with simple controls on how 49 * to scroll, and what content to load. 50 */ 51 public class ProfileActivity extends Activity { 52 53 private static final int TIMED_RECORD_MILLIS = 2000; 54 55 public interface ProfileCallback { 56 public void profileCallback(RunData data); 57 } 58 59 public static final String TEMP_FILENAME = "profile.tiles"; 60 61 Button mInspectButton; 62 ToggleButton mCaptureButton; 63 Spinner mVelocitySpinner; 64 Spinner mMovementSpinner; 65 EditText mUrl; 66 ProfiledWebView mWeb; 67 ProfileCallback mCallback; 68 69 LoggingWebViewClient mLoggingWebViewClient = new LoggingWebViewClient(); 70 AutoLoggingWebViewClient mAutoLoggingWebViewClient = new AutoLoggingWebViewClient(); 71 TimedLoggingWebViewClient mTimedLoggingWebViewClient = new TimedLoggingWebViewClient(); 72 73 private enum TestingState { 74 NOT_TESTING, 75 PRE_TESTING, 76 START_TESTING, 77 STOP_TESTING, 78 SAVED_TESTING 79 }; 80 81 private class VelocitySelectedListener implements OnItemSelectedListener { 82 @Override 83 public void onItemSelected(AdapterView<?> parent, View view, 84 int position, long id) { 85 String speedStr = parent.getItemAtPosition(position).toString(); 86 int speedInt = Integer.parseInt(speedStr); 87 mWeb.setAutoScrollSpeed(speedInt); 88 } 89 90 @Override 91 public void onNothingSelected(AdapterView<?> parent) { 92 } 93 } 94 95 private class MovementSelectedListener implements OnItemSelectedListener { 96 @Override 97 public void onItemSelected(AdapterView<?> parent, View view, 98 int position, long id) { 99 String movementStr = parent.getItemAtPosition(position).toString(); 100 if (movementStr == getResources().getString(R.string.movement_auto_scroll)) { 101 mWeb.setWebViewClient(mAutoLoggingWebViewClient); 102 mCaptureButton.setEnabled(false); 103 mVelocitySpinner.setEnabled(true); 104 } else if (movementStr == getResources().getString(R.string.movement_manual)) { 105 mWeb.setWebViewClient(mLoggingWebViewClient); 106 mCaptureButton.setEnabled(true); 107 mVelocitySpinner.setEnabled(false); 108 } else if (movementStr == getResources().getString(R.string.movement_timed)) { 109 mWeb.setWebViewClient(mTimedLoggingWebViewClient); 110 mCaptureButton.setEnabled(false); 111 mVelocitySpinner.setEnabled(false); 112 } 113 } 114 115 @Override 116 public void onNothingSelected(AdapterView<?> parent) { 117 } 118 } 119 120 private class LoggingWebViewClient extends WebViewClient { 121 @Override 122 public boolean shouldOverrideUrlLoading(WebView view, String url) { 123 return false; 124 } 125 126 @Override 127 public void onPageStarted(WebView view, String url, Bitmap favicon) { 128 super.onPageStarted(view, url, favicon); 129 mUrl.setText(url); 130 } 131 132 @Override 133 public void onPageFinished(WebView view, String url) { 134 super.onPageFinished(view, url); 135 view.requestFocus(); 136 ((ProfiledWebView)view).onPageFinished(); 137 } 138 } 139 140 private class AutoLoggingWebViewClient extends LoggingWebViewClient { 141 @Override 142 public void onPageFinished(WebView view, String url) { 143 super.onPageFinished(view, url); 144 startViewProfiling(true); 145 } 146 147 @Override 148 public void onPageStarted(WebView view, String url, Bitmap favicon) { 149 super.onPageStarted(view, url, favicon); 150 setTestingState(TestingState.PRE_TESTING); 151 } 152 } 153 154 private class TimedLoggingWebViewClient extends LoggingWebViewClient { 155 @Override 156 public void onPageFinished(WebView view, String url) { 157 super.onPageFinished(view, url); 158 startViewProfiling(false); 159 160 // after a fixed time after page finished, stop testing 161 new CountDownTimer(TIMED_RECORD_MILLIS, TIMED_RECORD_MILLIS) { 162 @Override 163 public void onTick(long millisUntilFinished) { 164 } 165 166 @Override 167 public void onFinish() { 168 mWeb.stopScrollTest(); 169 } 170 }.start(); 171 } 172 173 @Override 174 public void onPageStarted(WebView view, String url, Bitmap favicon) { 175 super.onPageStarted(view, url, favicon); 176 setTestingState(TestingState.PRE_TESTING); 177 } 178 } 179 180 private class StoreFileTask extends 181 AsyncTask<Pair<String, RunData>, Void, Void> { 182 183 @Override 184 protected Void doInBackground(Pair<String, RunData>... params) { 185 try { 186 FileOutputStream fos = openFileOutput(params[0].first, 187 Context.MODE_PRIVATE); 188 ObjectOutputStream out = new ObjectOutputStream(fos); 189 out.writeObject(params[0].second); 190 out.close(); 191 } catch (IOException ex) { 192 ex.printStackTrace(); 193 } 194 return null; 195 } 196 197 @Override 198 protected void onPostExecute(Void v) { 199 setTestingState(TestingState.SAVED_TESTING); 200 } 201 } 202 203 public void setTestingState(TestingState state) { 204 switch (state) { 205 case NOT_TESTING: 206 mUrl.setBackgroundResource(R.color.background_not_testing); 207 mInspectButton.setEnabled(true); 208 mMovementSpinner.setEnabled(true); 209 break; 210 case PRE_TESTING: 211 mInspectButton.setEnabled(false); 212 mMovementSpinner.setEnabled(false); 213 break; 214 case START_TESTING: 215 mCaptureButton.setChecked(true); 216 mUrl.setBackgroundResource(R.color.background_start_testing); 217 mInspectButton.setEnabled(false); 218 mMovementSpinner.setEnabled(false); 219 break; 220 case STOP_TESTING: 221 mCaptureButton.setChecked(false); 222 mUrl.setBackgroundResource(R.color.background_stop_testing); 223 break; 224 case SAVED_TESTING: 225 mInspectButton.setEnabled(true); 226 mMovementSpinner.setEnabled(true); 227 break; 228 } 229 } 230 231 /** auto - automatically scroll. */ 232 private void startViewProfiling(boolean auto) { 233 // toggle capture button to indicate capture state to user 234 mWeb.startScrollTest(mCallback, auto); 235 setTestingState(TestingState.START_TESTING); 236 } 237 238 /** Called when the activity is first created. */ 239 @Override 240 public void onCreate(Bundle savedInstanceState) { 241 super.onCreate(savedInstanceState); 242 setContentView(R.layout.main); 243 mInspectButton = (Button) findViewById(R.id.inspect); 244 mCaptureButton = (ToggleButton) findViewById(R.id.capture); 245 mVelocitySpinner = (Spinner) findViewById(R.id.velocity); 246 mMovementSpinner = (Spinner) findViewById(R.id.movement); 247 mUrl = (EditText) findViewById(R.id.url); 248 mWeb = (ProfiledWebView) findViewById(R.id.web); 249 setCallback(new ProfileCallback() { 250 @SuppressWarnings("unchecked") 251 @Override 252 public void profileCallback(RunData data) { 253 new StoreFileTask().execute(new Pair<String, RunData>( 254 TEMP_FILENAME, data)); 255 Log.d("ProfileActivity", "stored " + data.frames.length + " frames in file"); 256 setTestingState(TestingState.STOP_TESTING); 257 } 258 }); 259 260 // Inspect button (opens PlaybackActivity) 261 mInspectButton.setOnClickListener(new OnClickListener() { 262 @Override 263 public void onClick(View v) { 264 startActivity(new Intent(ProfileActivity.this, 265 PlaybackActivity.class)); 266 } 267 }); 268 269 // Velocity spinner 270 ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource( 271 this, R.array.velocity_array, 272 android.R.layout.simple_spinner_item); 273 adapter.setDropDownViewResource( 274 android.R.layout.simple_spinner_dropdown_item); 275 mVelocitySpinner.setAdapter(adapter); 276 mVelocitySpinner.setOnItemSelectedListener( 277 new VelocitySelectedListener()); 278 mVelocitySpinner.setSelection(3); 279 280 // Movement spinner 281 String content[] = { 282 getResources().getString(R.string.movement_auto_scroll), 283 getResources().getString(R.string.movement_manual), 284 getResources().getString(R.string.movement_timed) 285 }; 286 adapter = new ArrayAdapter<CharSequence>(this, 287 android.R.layout.simple_spinner_item, content); 288 adapter.setDropDownViewResource( 289 android.R.layout.simple_spinner_dropdown_item); 290 mMovementSpinner.setAdapter(adapter); 291 mMovementSpinner.setOnItemSelectedListener( 292 new MovementSelectedListener()); 293 mMovementSpinner.setSelection(0); 294 295 // Capture toggle button 296 mCaptureButton.setOnClickListener(new OnClickListener() { 297 @Override 298 public void onClick(View v) { 299 if (mCaptureButton.isChecked()) { 300 startViewProfiling(false); 301 } else { 302 mWeb.stopScrollTest(); 303 } 304 } 305 }); 306 307 // Custom profiling WebView 308 mWeb.init(this); 309 mWeb.setWebViewClient(new LoggingWebViewClient()); 310 311 // URL text entry 312 mUrl.setOnEditorActionListener(new OnEditorActionListener() { 313 public boolean onEditorAction(TextView v, int actionId, 314 KeyEvent event) { 315 String url = mUrl.getText().toString(); 316 mWeb.loadUrl(url); 317 mWeb.requestFocus(); 318 return true; 319 } 320 }); 321 322 setTestingState(TestingState.NOT_TESTING); 323 } 324 325 public void setCallback(ProfileCallback callback) { 326 mCallback = callback; 327 } 328 329 @Override 330 public boolean onKeyDown(int keyCode, KeyEvent event) { 331 if ((keyCode == KeyEvent.KEYCODE_BACK) && mWeb.canGoBack()) { 332 mWeb.goBack(); 333 return true; 334 } 335 return super.onKeyDown(keyCode, event); 336 } 337 } 338