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
17package com.test.tilebenchmark;
18
19import android.app.Activity;
20import android.content.Intent;
21import android.content.Context;
22import android.graphics.Bitmap;
23import android.os.AsyncTask;
24import android.os.Bundle;
25import android.os.CountDownTimer;
26import android.util.Log;
27import android.util.Pair;
28import android.view.KeyEvent;
29import android.view.View;
30import android.view.View.OnClickListener;
31import android.webkit.WebView;
32import android.webkit.WebViewClient;
33import android.widget.AdapterView;
34import android.widget.AdapterView.OnItemSelectedListener;
35import android.widget.ArrayAdapter;
36import android.widget.Button;
37import android.widget.EditText;
38import android.widget.Spinner;
39import android.widget.TextView;
40import android.widget.TextView.OnEditorActionListener;
41import android.widget.ToggleButton;
42
43import java.io.FileOutputStream;
44import java.io.IOException;
45import 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 */
51public 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