blob: 304c92730fb369f5900c38db14de25adc77cfa0e [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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 android.webkit;
18
19import android.app.AlertDialog;
20import android.content.Context;
21import android.content.DialogInterface;
22import android.content.Intent;
23import android.content.DialogInterface.OnCancelListener;
Leon Scroggins3667ce42009-05-13 15:58:03 -040024import android.database.DataSetObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.Color;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.graphics.Picture;
29import android.graphics.Point;
30import android.graphics.Rect;
31import android.graphics.Region;
Mike Reede8853fc2009-09-04 14:01:48 -040032import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.net.http.SslCertificate;
34import android.net.Uri;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.Message;
38import android.os.ServiceManager;
39import android.os.SystemClock;
40import android.provider.Checkin;
41import android.text.IClipboard;
42import android.text.Selection;
43import android.text.Spannable;
44import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.util.EventLog;
46import android.util.Log;
Leon Scroggins3a6c88c2009-09-09 14:50:23 -040047import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.view.Gravity;
49import android.view.KeyEvent;
50import android.view.LayoutInflater;
51import android.view.MotionEvent;
52import android.view.SoundEffectConstants;
53import android.view.VelocityTracker;
54import android.view.View;
55import android.view.ViewConfiguration;
56import android.view.ViewGroup;
57import android.view.ViewParent;
58import android.view.ViewTreeObserver;
The Android Open Source Project10592532009-03-18 17:39:46 -070059import android.view.animation.AlphaAnimation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.view.inputmethod.InputMethodManager;
Leon Scrogginsd3465f62009-06-02 10:57:54 -040061import android.webkit.WebTextView.AutoCompleteAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.webkit.WebViewCore.EventHub;
63import android.widget.AbsoluteLayout;
Leon Scroggins3667ce42009-05-13 15:58:03 -040064import android.widget.Adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import android.widget.AdapterView;
66import android.widget.ArrayAdapter;
Leon Scrogginsa8da1732009-10-19 19:04:30 -040067import android.widget.CheckedTextView;
The Android Open Source Project10592532009-03-18 17:39:46 -070068import android.widget.FrameLayout;
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040069import android.widget.LinearLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.widget.Scroller;
72import android.widget.Toast;
73import android.widget.ZoomButtonsController;
The Android Open Source Project10592532009-03-18 17:39:46 -070074import android.widget.ZoomControls;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import android.widget.AdapterView.OnItemClickListener;
76
77import java.io.File;
78import java.io.FileInputStream;
79import java.io.FileNotFoundException;
80import java.io.FileOutputStream;
81import java.io.IOException;
82import java.net.URLDecoder;
83import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import java.util.List;
Andrei Popescu4950b2b2009-09-03 13:56:07 +010085import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040087import junit.framework.Assert;
88
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089/**
Cary Clarkd6982c92009-05-29 11:02:22 -040090 * <p>A View that displays web pages. This class is the basis upon which you
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 * can roll your own web browser or simply display some online content within your Activity.
92 * It uses the WebKit rendering engine to display
93 * web pages and includes methods to navigate forward and backward
94 * through a history, zoom in and out, perform text searches and more.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -070095 * <p>To enable the built-in zoom, set
96 * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
97 * (introduced in API version 3).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 * <p>Note that, in order for your Activity to access the Internet and load web pages
Cary Clarkd6982c92009-05-29 11:02:22 -040099 * in a WebView, you must add the <var>INTERNET</var> permissions to your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 * Android Manifest file:</p>
101 * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100102 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 * <p>This must be a child of the <code>&lt;manifest></code> element.</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100104 *
105 * <h3>Basic usage</h3>
106 *
107 * <p>By default, a WebView provides no browser-like widgets, does not
108 * enable JavaScript and errors will be ignored. If your goal is only
109 * to display some HTML as a part of your UI, this is probably fine;
110 * the user won't need to interact with the web page beyond reading
111 * it, and the web page won't need to interact with the user. If you
112 * actually want a fully blown web browser, then you probably want to
113 * invoke the Browser application with your URL rather than show it
114 * with a WebView. See {@link android.content.Intent} for more information.</p>
115 *
116 * <pre class="prettyprint">
117 * WebView webview = new WebView(this);
118 * setContentView(webview);
119 *
120 * // Simplest usage: note that an exception will NOT be thrown
121 * // if there is an error loading this page (see below).
122 * webview.loadUrl("http://slashdot.org/");
123 *
124 * // Of course you can also load from any string:
125 * String summary = "&lt;html>&lt;body>You scored &lt;b>192</b> points.&lt;/body>&lt;/html>";
126 * webview.loadData(summary, "text/html", "utf-8");
127 * // ... although note that there are restrictions on what this HTML can do.
128 * // See the JavaDocs for loadData and loadDataWithBaseUrl for more info.
129 * </pre>
130 *
131 * <p>A WebView has several customization points where you can add your
132 * own behavior. These are:</p>
133 *
134 * <ul>
135 * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
136 * This class is called when something that might impact a
137 * browser UI happens, for instance, progress updates and
138 * JavaScript alerts are sent here.
139 * </li>
140 * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
141 * It will be called when things happen that impact the
142 * rendering of the content, eg, errors or form submissions. You
143 * can also intercept URL loading here.</li>
144 * <li>Via the {@link android.webkit.WebSettings} class, which contains
145 * miscellaneous configuration. </li>
146 * <li>With the {@link android.webkit.WebView#addJavascriptInterface} method.
147 * This lets you bind Java objects into the WebView so they can be
148 * controlled from the web pages JavaScript.</li>
149 * </ul>
150 *
151 * <p>Here's a more complicated example, showing error handling,
152 * settings, and progress notification:</p>
153 *
154 * <pre class="prettyprint">
155 * // Let's display the progress in the activity title bar, like the
156 * // browser app does.
157 * getWindow().requestFeature(Window.FEATURE_PROGRESS);
158 *
159 * webview.getSettings().setJavaScriptEnabled(true);
160 *
161 * final Activity activity = this;
162 * webview.setWebChromeClient(new WebChromeClient() {
163 * public void onProgressChanged(WebView view, int progress) {
164 * // Activities and WebViews measure progress with different scales.
165 * // The progress meter will automatically disappear when we reach 100%
166 * activity.setProgress(progress * 1000);
167 * }
168 * });
169 * webview.setWebViewClient(new WebViewClient() {
170 * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
171 * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
172 * }
173 * });
174 *
175 * webview.loadUrl("http://slashdot.org/");
176 * </pre>
177 *
178 * <h3>Cookie and window management</h3>
179 *
180 * <p>For obvious security reasons, your application has its own
181 * cache, cookie store etc - it does not share the Browser
182 * applications data. Cookies are managed on a separate thread, so
183 * operations like index building don't block the UI
184 * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
185 * if you want to use cookies in your application.
186 * </p>
187 *
188 * <p>By default, requests by the HTML to open new windows are
189 * ignored. This is true whether they be opened by JavaScript or by
190 * the target attribute on a link. You can customize your
191 * WebChromeClient to provide your own behaviour for opening multiple windows,
192 * and render them in whatever manner you want.</p>
193 *
194 * <p>Standard behavior for an Activity is to be destroyed and
195 * recreated when the devices orientation is changed. This will cause
196 * the WebView to reload the current page. If you don't want that, you
197 * can set your Activity to handle the orientation and keyboardHidden
198 * changes, and then just leave the WebView alone. It'll automatically
199 * re-orient itself as appropriate.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 */
Cary Clarkd6982c92009-05-29 11:02:22 -0400201public class WebView extends AbsoluteLayout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 implements ViewTreeObserver.OnGlobalFocusChangeListener,
203 ViewGroup.OnHierarchyChangeListener {
204
205 // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
206 // the screen all-the-time. Good for profiling our drawing code
207 static private final boolean AUTO_REDRAW_HACK = false;
208 // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
209 private boolean mAutoRedraw;
210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 static final String LOGTAG = "webview";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212
Leon Scroggins213a31c2009-05-21 16:49:32 -0700213 private static class ExtendedZoomControls extends FrameLayout {
The Android Open Source Project10592532009-03-18 17:39:46 -0700214 public ExtendedZoomControls(Context context, AttributeSet attrs) {
215 super(context, attrs);
216 LayoutInflater inflater = (LayoutInflater)
217 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
218 inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400219 mPlusMinusZoomControls = (ZoomControls) findViewById(
220 com.android.internal.R.id.zoomControls);
Grace Kloba04b28682009-09-14 14:38:37 -0700221 findViewById(com.android.internal.R.id.zoomMagnify).setVisibility(
222 View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700223 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400224
The Android Open Source Project10592532009-03-18 17:39:46 -0700225 public void show(boolean showZoom, boolean canZoomOut) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400226 mPlusMinusZoomControls.setVisibility(
227 showZoom ? View.VISIBLE : View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700228 fade(View.VISIBLE, 0.0f, 1.0f);
229 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400230
The Android Open Source Project10592532009-03-18 17:39:46 -0700231 public void hide() {
232 fade(View.GONE, 1.0f, 0.0f);
233 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400234
The Android Open Source Project10592532009-03-18 17:39:46 -0700235 private void fade(int visibility, float startAlpha, float endAlpha) {
236 AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
237 anim.setDuration(500);
238 startAnimation(anim);
239 setVisibility(visibility);
240 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400241
The Android Open Source Project10592532009-03-18 17:39:46 -0700242 public boolean hasFocus() {
Grace Kloba04b28682009-09-14 14:38:37 -0700243 return mPlusMinusZoomControls.hasFocus();
The Android Open Source Project10592532009-03-18 17:39:46 -0700244 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400245
The Android Open Source Project10592532009-03-18 17:39:46 -0700246 public void setOnZoomInClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400247 mPlusMinusZoomControls.setOnZoomInClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700248 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400249
The Android Open Source Project10592532009-03-18 17:39:46 -0700250 public void setOnZoomOutClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400251 mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700252 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400253
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400254 ZoomControls mPlusMinusZoomControls;
The Android Open Source Project10592532009-03-18 17:39:46 -0700255 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400256
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 /**
258 * Transportation object for returning WebView across thread boundaries.
259 */
260 public class WebViewTransport {
261 private WebView mWebview;
262
263 /**
264 * Set the WebView to the transportation object.
265 * @param webview The WebView to transport.
266 */
267 public synchronized void setWebView(WebView webview) {
268 mWebview = webview;
269 }
270
271 /**
272 * Return the WebView object.
273 * @return WebView The transported WebView object.
274 */
275 public synchronized WebView getWebView() {
276 return mWebview;
277 }
278 }
279
280 // A final CallbackProxy shared by WebViewCore and BrowserFrame.
281 private final CallbackProxy mCallbackProxy;
282
283 private final WebViewDatabase mDatabase;
284
285 // SSL certificate for the main top-level page (if secure)
286 private SslCertificate mCertificate;
287
288 // Native WebView pointer that is 0 until the native object has been
289 // created.
290 private int mNativeClass;
291 // This would be final but it needs to be set to null when the WebView is
292 // destroyed.
293 private WebViewCore mWebViewCore;
294 // Handler for dispatching UI messages.
295 /* package */ final Handler mPrivateHandler = new PrivateHandler();
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400296 private WebTextView mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 // Used to ignore changes to webkit text that arrives to the UI side after
298 // more key events.
299 private int mTextGeneration;
300
Patrick Scott0a5ce012009-07-02 08:56:10 -0400301 // Used by WebViewCore to create child views.
302 /* package */ final ViewManager mViewManager;
303
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 /**
305 * Position of the last touch event.
306 */
307 private float mLastTouchX;
308 private float mLastTouchY;
309
310 /**
311 * Time of the last touch event.
312 */
313 private long mLastTouchTime;
314
315 /**
316 * Time of the last time sending touch event to WebViewCore
317 */
318 private long mLastSentTouchTime;
319
320 /**
321 * The minimum elapsed time before sending another ACTION_MOVE event to
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700322 * WebViewCore. This really should be tuned for each type of the devices.
323 * For example in Google Map api test case, it takes Dream device at least
324 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
325 * triggering the layout and drawing the picture. While the same process
326 * takes 60+ms on the current high speed device. If we make
327 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
328 * to WebViewCore queue and the real layout and draw events will be pushed
329 * to further, which slows down the refresh rate. Choose 50 to favor the
330 * current high speed devices. For Dream like devices, 100 is a better
331 * choice. Maybe make this in the buildspec later.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 */
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700333 private static final int TOUCH_SENT_INTERVAL = 50;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334
335 /**
336 * Helper class to get velocity for fling
337 */
338 VelocityTracker mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700339 private int mMaximumFling;
Cary Clark278ce052009-08-31 16:08:42 -0400340 private float mLastVelocity;
341 private float mLastVelX;
342 private float mLastVelY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800344 /**
345 * Touch mode
346 */
347 private int mTouchMode = TOUCH_DONE_MODE;
348 private static final int TOUCH_INIT_MODE = 1;
349 private static final int TOUCH_DRAG_START_MODE = 2;
350 private static final int TOUCH_DRAG_MODE = 3;
351 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
352 private static final int TOUCH_SHORTPRESS_MODE = 5;
Grace Kloba04b28682009-09-14 14:38:37 -0700353 private static final int TOUCH_DOUBLE_TAP_MODE = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 private static final int TOUCH_DONE_MODE = 7;
355 private static final int TOUCH_SELECT_MODE = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356
357 // Whether to forward the touch events to WebCore
358 private boolean mForwardTouchEvents = false;
359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 // Whether to prevent drag during touch. The initial value depends on
361 // mForwardTouchEvents. If WebCore wants touch events, we assume it will
362 // take control of touch events unless it says no for touch down event.
Grace Klobaf58af622009-09-24 17:41:23 -0700363 private static final int PREVENT_DRAG_NO = 0;
364 private static final int PREVENT_DRAG_MAYBE_YES = 1;
365 private static final int PREVENT_DRAG_YES = 2;
366 private int mPreventDrag = PREVENT_DRAG_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367
Leon Scroggins72543e12009-07-23 15:29:45 -0400368 // To keep track of whether the current drag was initiated by a WebTextView,
369 // so that we know not to hide the cursor
370 boolean mDragFromTextInput;
371
Cary Clarkd6982c92009-05-29 11:02:22 -0400372 // Whether or not to draw the cursor ring.
373 private boolean mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800374
Mike Reedd205d5b2009-05-27 11:02:29 -0400375 // true if onPause has been called (and not onResume)
376 private boolean mIsPaused;
377
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 /**
379 * Customizable constant
380 */
381 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
382 private int mTouchSlopSquare;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700383 // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
384 private int mDoubleTapSlopSquare;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700385 // pre-computed density adjusted navigation slop
386 private int mNavSlop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387 // This should be ViewConfiguration.getTapTimeout()
388 // But system time out is 100ms, which is too short for the browser.
389 // In the browser, if it switches out of tap too soon, jump tap won't work.
390 private static final int TAP_TIMEOUT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 // This should be ViewConfiguration.getLongPressTimeout()
392 // But system time out is 500ms, which is too short for the browser.
393 // With a short timeout, it's difficult to treat trigger a short press.
394 private static final int LONG_PRESS_TIMEOUT = 1000;
395 // needed to avoid flinging after a pause of no movement
396 private static final int MIN_FLING_TIME = 250;
Cary Clark25415e22009-10-12 13:41:28 -0400397 // draw unfiltered after drag is held without movement
398 private static final int MOTIONLESS_TIME = 100;
The Android Open Source Project10592532009-03-18 17:39:46 -0700399 // The time that the Zoom Controls are visible before fading away
Cary Clarkd6982c92009-05-29 11:02:22 -0400400 private static final long ZOOM_CONTROLS_TIMEOUT =
The Android Open Source Project10592532009-03-18 17:39:46 -0700401 ViewConfiguration.getZoomControlsTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800402 // The amount of content to overlap between two screens when going through
403 // pages with the space bar, in pixels.
404 private static final int PAGE_SCROLL_OVERLAP = 24;
405
406 /**
407 * These prevent calling requestLayout if either dimension is fixed. This
408 * depends on the layout parameters and the measure specs.
409 */
410 boolean mWidthCanMeasure;
411 boolean mHeightCanMeasure;
412
413 // Remember the last dimensions we sent to the native side so we can avoid
414 // sending the same dimensions more than once.
415 int mLastWidthSent;
416 int mLastHeightSent;
417
418 private int mContentWidth; // cache of value from WebViewCore
419 private int mContentHeight; // cache of value from WebViewCore
420
Cary Clarkd6982c92009-05-29 11:02:22 -0400421 // Need to have the separate control for horizontal and vertical scrollbar
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 // style than the View's single scrollbar style
423 private boolean mOverlayHorizontalScrollbar = true;
424 private boolean mOverlayVerticalScrollbar = false;
425
426 // our standard speed. this way small distances will be traversed in less
427 // time than large distances, but we cap the duration, so that very large
428 // distances won't take too long to get there.
429 private static final int STD_SPEED = 480; // pixels per second
430 // time for the longest scroll animation
431 private static final int MAX_DURATION = 750; // milliseconds
Leon Scroggins03c87bf2009-09-18 15:05:59 -0400432 private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 private Scroller mScroller;
434
435 private boolean mWrapContent;
Cary Clark25415e22009-10-12 13:41:28 -0400436 private static final int MOTIONLESS_FALSE = 0;
437 private static final int MOTIONLESS_PENDING = 1;
438 private static final int MOTIONLESS_TRUE = 2;
439 private int mHeldMotionless;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800441 /**
442 * Private message ids
443 */
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400444 private static final int REMEMBER_PASSWORD = 1;
445 private static final int NEVER_REMEMBER_PASSWORD = 2;
446 private static final int SWITCH_TO_SHORTPRESS = 3;
447 private static final int SWITCH_TO_LONGPRESS = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700448 private static final int RELEASE_SINGLE_TAP = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400449 private static final int REQUEST_FORM_DATA = 6;
Grace Klobadd817492009-09-14 10:17:06 -0700450 private static final int RESUME_WEBCORE_UPDATE = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400451 private static final int DRAG_HELD_MOTIONLESS = 8;
452 private static final int AWAKEN_SCROLL_BARS = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453
454 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400455 static final int SCROLL_TO_MSG_ID = 10;
456 static final int SCROLL_BY_MSG_ID = 11;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400458 static final int SPAWN_SCROLL_TO_MSG_ID = 12;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800459 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400460 static final int SYNC_SCROLL_TO_MSG_ID = 13;
461 static final int NEW_PICTURE_MSG_ID = 14;
462 static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
463 static final int WEBCORE_INITIALIZED_MSG_ID = 16;
464 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
Cary Clark215b72c2009-06-26 14:38:43 -0400465 static final int MOVE_OUT_OF_PLUGIN = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400466 static final int CLEAR_TEXT_ENTRY = 20;
Leon Scroggins6679f2f2009-08-12 18:48:10 -0400467 static final int UPDATE_TEXT_SELECTION_MSG_ID = 21;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400468 static final int UPDATE_CLIPBOARD = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400469 static final int LONG_PRESS_CENTER = 23;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400470 static final int PREVENT_TOUCH_ID = 24;
471 static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 // obj=Rect in doc coordinates
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400473 static final int INVAL_RECT_MSG_ID = 26;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400474 static final int REQUEST_KEYBOARD = 27;
Cary Clarkd6982c92009-05-29 11:02:22 -0400475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 static final String[] HandlerDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400477 "REMEMBER_PASSWORD", // = 1;
478 "NEVER_REMEMBER_PASSWORD", // = 2;
479 "SWITCH_TO_SHORTPRESS", // = 3;
480 "SWITCH_TO_LONGPRESS", // = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700481 "RELEASE_SINGLE_TAP", // = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400482 "REQUEST_FORM_DATA", // = 6;
Cary Clark25415e22009-10-12 13:41:28 -0400483 "RESUME_WEBCORE_UPDATE", // = 7;
484 "DRAG_HELD_MOTIONLESS", // = 8;
485 "AWAKEN_SCROLL_BARS", // = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800486 "SCROLL_TO_MSG_ID", // = 10;
487 "SCROLL_BY_MSG_ID", // = 11;
488 "SPAWN_SCROLL_TO_MSG_ID", // = 12;
489 "SYNC_SCROLL_TO_MSG_ID", // = 13;
490 "NEW_PICTURE_MSG_ID", // = 14;
491 "UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
492 "WEBCORE_INITIALIZED_MSG_ID", // = 16;
493 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
Grace Klobaef347ef2009-07-30 11:20:32 -0700494 "18", // = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400495 "MOVE_OUT_OF_PLUGIN", // = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400496 "CLEAR_TEXT_ENTRY", // = 20;
Leon Scroggins6679f2f2009-08-12 18:48:10 -0400497 "UPDATE_TEXT_SELECTION_MSG_ID", // = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 "UPDATE_CLIPBOARD", // = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400499 "LONG_PRESS_CENTER", // = 23;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 "PREVENT_TOUCH_ID", // = 24;
501 "WEBCORE_NEED_TOUCH_EVENTS", // = 25;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400502 "INVAL_RECT_MSG_ID", // = 26;
503 "REQUEST_KEYBOARD" // = 27;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 };
505
Grace Kloba25737912009-06-19 12:42:47 -0700506 // default scale limit. Depending on the display density
507 private static float DEFAULT_MAX_ZOOM_SCALE;
508 private static float DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 // scale limit, which can be set through viewport meta tag in the web page
Grace Kloba25737912009-06-19 12:42:47 -0700510 private float mMaxZoomScale;
511 private float mMinZoomScale;
Grace Klobae397a882009-08-06 12:04:14 -0700512 private boolean mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513
514 // initial scale in percent. 0 means using default.
515 private int mInitialScale = 0;
516
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700517 // while in the zoom overview mode, the page's width is fully fit to the
518 // current window. The page is alive, in another words, you can click to
519 // follow the links. Double tap will toggle between zoom overview mode and
520 // the last zoom scale.
521 boolean mInZoomOverview = false;
Leon Scrogginsf58ffac2009-08-13 15:34:06 -0400522
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700523 // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
524 // engadget always have wider mContentWidth no matter what viewport size is.
Grace Klobae397a882009-08-06 12:04:14 -0700525 int mZoomOverviewWidth = WebViewCore.DEFAULT_VIEWPORT_WIDTH;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700526 float mLastScale;
527
Grace Kloba25737912009-06-19 12:42:47 -0700528 // default scale. Depending on the display density.
529 static int DEFAULT_SCALE_PERCENT;
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700530 private float mDefaultScale;
Grace Kloba25737912009-06-19 12:42:47 -0700531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 // set to true temporarily while the zoom control is being dragged
533 private boolean mPreviewZoomOnly = false;
534
535 // computed scale and inverse, from mZoomWidth.
Grace Kloba25737912009-06-19 12:42:47 -0700536 private float mActualScale;
537 private float mInvActualScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800538 // if this is non-zero, it is used on drawing rather than mActualScale
539 private float mZoomScale;
540 private float mInvInitialZoomScale;
541 private float mInvFinalZoomScale;
Grace Kloba675c7d22009-07-23 09:21:21 -0700542 private int mInitialScrollX;
543 private int mInitialScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800544 private long mZoomStart;
545 private static final int ZOOM_ANIMATION_LENGTH = 500;
546
547 private boolean mUserScroll = false;
548
549 private int mSnapScrollMode = SNAP_NONE;
Cary Clarkac492e12009-10-14 14:53:37 -0400550 private static final int SNAP_NONE = 0;
551 private static final int SNAP_LOCK = 1; // not a separate state
552 private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
553 private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800554 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800556 // Used to match key downs and key ups
557 private boolean mGotKeyDown;
558
559 /* package */ static boolean mLogEvent = true;
560 private static final int EVENT_LOG_ZOOM_LEVEL_CHANGE = 70101;
561 private static final int EVENT_LOG_DOUBLE_TAP_DURATION = 70102;
562
563 // for event log
564 private long mLastTouchUpTime = 0;
565
566 /**
567 * URI scheme for telephone number
568 */
569 public static final String SCHEME_TEL = "tel:";
570 /**
571 * URI scheme for email address
572 */
573 public static final String SCHEME_MAILTO = "mailto:";
574 /**
575 * URI scheme for map address
576 */
577 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400578
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800579 private int mBackgroundColor = Color.WHITE;
580
581 // Used to notify listeners of a new picture.
582 private PictureListener mPictureListener;
583 /**
584 * Interface to listen for new pictures as they change.
585 */
586 public interface PictureListener {
587 /**
588 * Notify the listener that the picture has changed.
589 * @param view The WebView that owns the picture.
590 * @param picture The new picture.
591 */
592 public void onNewPicture(WebView view, Picture picture);
593 }
594
Leon Scroggins3246c222009-05-26 09:51:23 -0400595 // FIXME: Want to make this public, but need to change the API file.
596 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 /**
598 * Default HitTestResult, where the target is unknown
599 */
600 public static final int UNKNOWN_TYPE = 0;
601 /**
602 * HitTestResult for hitting a HTML::a tag
603 */
604 public static final int ANCHOR_TYPE = 1;
605 /**
606 * HitTestResult for hitting a phone number
607 */
608 public static final int PHONE_TYPE = 2;
609 /**
610 * HitTestResult for hitting a map address
611 */
612 public static final int GEO_TYPE = 3;
613 /**
614 * HitTestResult for hitting an email address
615 */
616 public static final int EMAIL_TYPE = 4;
617 /**
618 * HitTestResult for hitting an HTML::img tag
619 */
620 public static final int IMAGE_TYPE = 5;
621 /**
622 * HitTestResult for hitting a HTML::a tag which contains HTML::img
623 */
624 public static final int IMAGE_ANCHOR_TYPE = 6;
625 /**
626 * HitTestResult for hitting a HTML::a tag with src=http
627 */
628 public static final int SRC_ANCHOR_TYPE = 7;
629 /**
630 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
631 */
632 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
633 /**
634 * HitTestResult for hitting an edit text area
635 */
636 public static final int EDIT_TEXT_TYPE = 9;
637
638 private int mType;
639 private String mExtra;
640
641 HitTestResult() {
642 mType = UNKNOWN_TYPE;
643 }
644
645 private void setType(int type) {
646 mType = type;
647 }
648
649 private void setExtra(String extra) {
650 mExtra = extra;
651 }
652
653 public int getType() {
654 return mType;
655 }
656
657 public String getExtra() {
658 return mExtra;
659 }
660 }
661
The Android Open Source Project10592532009-03-18 17:39:46 -0700662 // The View containing the zoom controls
663 private ExtendedZoomControls mZoomControls;
664 private Runnable mZoomControlRunnable;
665
Cary Clarkd6982c92009-05-29 11:02:22 -0400666 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700668 // These keep track of the center point of the zoom. They are used to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 // determine the point around which we should zoom.
670 private float mZoomCenterX;
671 private float mZoomCenterY;
672
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700673 private ZoomButtonsController.OnZoomListener mZoomListener =
674 new ZoomButtonsController.OnZoomListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 public void onVisibilityChanged(boolean visible) {
677 if (visible) {
678 switchOutDrawHistory();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700679 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800680 }
681 }
682
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700683 public void onZoom(boolean zoomIn) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 if (zoomIn) {
685 zoomIn();
686 } else {
687 zoomOut();
688 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400689
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700690 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 };
Cary Clarkd6982c92009-05-29 11:02:22 -0400693
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 /**
695 * Construct a new WebView with a Context object.
696 * @param context A Context object used to access application assets.
697 */
698 public WebView(Context context) {
699 this(context, null);
700 }
701
702 /**
703 * Construct a new WebView with layout parameters.
704 * @param context A Context object used to access application assets.
705 * @param attrs An AttributeSet passed to our parent.
706 */
707 public WebView(Context context, AttributeSet attrs) {
708 this(context, attrs, com.android.internal.R.attr.webViewStyle);
709 }
710
711 /**
712 * Construct a new WebView with layout parameters and a default style.
713 * @param context A Context object used to access application assets.
714 * @param attrs An AttributeSet passed to our parent.
715 * @param defStyle The default style resource ID.
716 */
717 public WebView(Context context, AttributeSet attrs, int defStyle) {
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100718 this(context, attrs, defStyle, null);
719 }
720
721 /**
722 * Construct a new WebView with layout parameters, a default style and a set
723 * of custom Javscript interfaces to be added to the WebView at initialization
724 * time. This guraratees that these interfaces will be available when the JS
725 * context is initialized.
726 * @param context A Context object used to access application assets.
727 * @param attrs An AttributeSet passed to our parent.
728 * @param defStyle The default style resource ID.
729 * @param javascriptInterfaces is a Map of intareface names, as keys, and
730 * object implementing those interfaces, as values.
731 * @hide pending API council approval.
732 */
733 protected WebView(Context context, AttributeSet attrs, int defStyle,
734 Map<String, Object> javascriptInterfaces) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 super(context, attrs, defStyle);
736 init();
737
738 mCallbackProxy = new CallbackProxy(context, this);
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100739 mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 mDatabase = WebViewDatabase.getInstance(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 mScroller = new Scroller(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700742
Patrick Scott0a5ce012009-07-02 08:56:10 -0400743 mViewManager = new ViewManager(this);
744
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700745 mZoomButtonsController = new ZoomButtonsController(this);
746 mZoomButtonsController.setOnZoomListener(mZoomListener);
Leon Scrogginsaa3f96a2009-06-11 14:46:35 -0400747 // ZoomButtonsController positions the buttons at the bottom, but in
748 // the middle. Change their layout parameters so they appear on the
749 // right.
750 View controls = mZoomButtonsController.getZoomControls();
751 ViewGroup.LayoutParams params = controls.getLayoutParams();
752 if (params instanceof FrameLayout.LayoutParams) {
753 FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
754 params;
755 frameParams.gravity = Gravity.RIGHT;
756 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700757 }
758
759 private void updateZoomButtonsEnabled() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700760 boolean canZoomIn = mActualScale < mMaxZoomScale;
Grace Kloba455e3af2009-08-13 11:01:21 -0700761 boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview;
The Android Open Source Project10592532009-03-18 17:39:46 -0700762 if (!canZoomIn && !canZoomOut) {
763 // Hide the zoom in and out buttons, as well as the fit to page
764 // button, if the page cannot zoom
765 mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700766 } else {
767 // Bring back the hidden zoom controls.
768 mZoomButtonsController.getZoomControls()
769 .setVisibility(View.VISIBLE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700770 // Set each one individually, as a page may be able to zoom in
771 // or out.
772 mZoomButtonsController.setZoomInEnabled(canZoomIn);
773 mZoomButtonsController.setZoomOutEnabled(canZoomOut);
The Android Open Source Project10592532009-03-18 17:39:46 -0700774 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 }
776
777 private void init() {
778 setWillNotDraw(false);
779 setFocusable(true);
780 setFocusableInTouchMode(true);
781 setClickable(true);
782 setLongClickable(true);
783
Romain Guy4296fc42009-07-06 11:48:52 -0700784 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700785 int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 mTouchSlopSquare = slop * slop;
787 mMinLockSnapReverseDistance = slop;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700788 slop = configuration.getScaledDoubleTapSlop();
789 mDoubleTapSlopSquare = slop * slop;
Grace Kloba25737912009-06-19 12:42:47 -0700790 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700791 // use one line height, 16 based on our current default font, for how
792 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -0700793 mNavSlop = (int) (16 * density);
794 // density adjusted scale factors
795 DEFAULT_SCALE_PERCENT = (int) (100 * density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700796 mDefaultScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700797 mActualScale = density;
798 mInvActualScale = 1 / density;
799 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
800 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
801 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
802 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
Romain Guy4296fc42009-07-06 11:48:52 -0700803 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
805
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700806 /* package */void updateDefaultZoomDensity(int zoomDensity) {
807 final float density = getContext().getResources().getDisplayMetrics().density
808 * 100 / zoomDensity;
809 if (Math.abs(density - mDefaultScale) > 0.01) {
810 float scaleFactor = density / mDefaultScale;
811 // adjust the limits
812 mNavSlop = (int) (16 * density);
813 DEFAULT_SCALE_PERCENT = (int) (100 * density);
814 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
815 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
816 mDefaultScale = density;
817 mMaxZoomScale *= scaleFactor;
818 mMinZoomScale *= scaleFactor;
819 setNewZoomScale(mActualScale * scaleFactor, false);
820 }
821 }
822
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800823 /* package */ boolean onSavePassword(String schemePlusHost, String username,
824 String password, final Message resumeMsg) {
825 boolean rVal = false;
826 if (resumeMsg == null) {
827 // null resumeMsg implies saving password silently
828 mDatabase.setUsernamePassword(schemePlusHost, username, password);
829 } else {
830 final Message remember = mPrivateHandler.obtainMessage(
831 REMEMBER_PASSWORD);
832 remember.getData().putString("host", schemePlusHost);
833 remember.getData().putString("username", username);
834 remember.getData().putString("password", password);
835 remember.obj = resumeMsg;
836
837 final Message neverRemember = mPrivateHandler.obtainMessage(
838 NEVER_REMEMBER_PASSWORD);
839 neverRemember.getData().putString("host", schemePlusHost);
840 neverRemember.getData().putString("username", username);
841 neverRemember.getData().putString("password", password);
842 neverRemember.obj = resumeMsg;
843
844 new AlertDialog.Builder(getContext())
845 .setTitle(com.android.internal.R.string.save_password_label)
846 .setMessage(com.android.internal.R.string.save_password_message)
847 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
848 new DialogInterface.OnClickListener() {
849 public void onClick(DialogInterface dialog, int which) {
850 resumeMsg.sendToTarget();
851 }
852 })
853 .setNeutralButton(com.android.internal.R.string.save_password_remember,
854 new DialogInterface.OnClickListener() {
855 public void onClick(DialogInterface dialog, int which) {
856 remember.sendToTarget();
857 }
858 })
859 .setNegativeButton(com.android.internal.R.string.save_password_never,
860 new DialogInterface.OnClickListener() {
861 public void onClick(DialogInterface dialog, int which) {
862 neverRemember.sendToTarget();
863 }
864 })
865 .setOnCancelListener(new OnCancelListener() {
866 public void onCancel(DialogInterface dialog) {
867 resumeMsg.sendToTarget();
868 }
869 }).show();
870 // Return true so that WebViewCore will pause while the dialog is
871 // up.
872 rVal = true;
873 }
874 return rVal;
875 }
876
877 @Override
878 public void setScrollBarStyle(int style) {
879 if (style == View.SCROLLBARS_INSIDE_INSET
880 || style == View.SCROLLBARS_OUTSIDE_INSET) {
881 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
882 } else {
883 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
884 }
885 super.setScrollBarStyle(style);
886 }
887
888 /**
889 * Specify whether the horizontal scrollbar has overlay style.
890 * @param overlay TRUE if horizontal scrollbar should have overlay style.
891 */
892 public void setHorizontalScrollbarOverlay(boolean overlay) {
893 mOverlayHorizontalScrollbar = overlay;
894 }
895
896 /**
897 * Specify whether the vertical scrollbar has overlay style.
898 * @param overlay TRUE if vertical scrollbar should have overlay style.
899 */
900 public void setVerticalScrollbarOverlay(boolean overlay) {
901 mOverlayVerticalScrollbar = overlay;
902 }
903
904 /**
905 * Return whether horizontal scrollbar has overlay style
906 * @return TRUE if horizontal scrollbar has overlay style.
907 */
908 public boolean overlayHorizontalScrollbar() {
909 return mOverlayHorizontalScrollbar;
910 }
911
912 /**
913 * Return whether vertical scrollbar has overlay style
914 * @return TRUE if vertical scrollbar has overlay style.
915 */
916 public boolean overlayVerticalScrollbar() {
917 return mOverlayVerticalScrollbar;
918 }
919
920 /*
921 * Return the width of the view where the content of WebView should render
922 * to.
Grace Kloba6ed525e2009-09-17 15:31:12 -0700923 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 */
Grace Kloba6ed525e2009-09-17 15:31:12 -0700925 /* package */ int getViewWidth() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
927 return getWidth();
928 } else {
929 return getWidth() - getVerticalScrollbarWidth();
930 }
931 }
932
933 /*
Mike Reede8853fc2009-09-04 14:01:48 -0400934 * returns the height of the titlebarview (if any). Does not care about
935 * scrolling
936 */
937 private int getTitleHeight() {
938 return mTitleBar != null ? mTitleBar.getHeight() : 0;
939 }
940
941 /*
942 * Return the amount of the titlebarview (if any) that is visible
943 */
944 private int getVisibleTitleHeight() {
945 return Math.max(getTitleHeight() - mScrollY, 0);
946 }
947
948 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 * Return the height of the view where the content of WebView should render
Leon Scroggins0236e672009-09-02 21:12:08 -0400950 * to. Note that this excludes mTitleBar, if there is one.
Grace Kloba6ed525e2009-09-17 15:31:12 -0700951 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 */
Grace Kloba6ed525e2009-09-17 15:31:12 -0700953 /* package */ int getViewHeight() {
Grace Kloba8eff73f2009-09-24 09:34:32 -0700954 return getViewHeightWithTitle() - getVisibleTitleHeight();
955 }
956
957 private int getViewHeightWithTitle() {
Leon Scroggins0236e672009-09-02 21:12:08 -0400958 int height = getHeight();
Mike Reede8853fc2009-09-04 14:01:48 -0400959 if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
Leon Scroggins0236e672009-09-02 21:12:08 -0400960 height -= getHorizontalScrollbarHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 }
Grace Kloba8eff73f2009-09-24 09:34:32 -0700962 return height;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 }
964
965 /**
966 * @return The SSL certificate for the main top-level page or null if
967 * there is no certificate (the site is not secure).
968 */
969 public SslCertificate getCertificate() {
970 return mCertificate;
971 }
972
973 /**
974 * Sets the SSL certificate for the main top-level page.
975 */
976 public void setCertificate(SslCertificate certificate) {
977 // here, the certificate can be null (if the site is not secure)
978 mCertificate = certificate;
979 }
980
981 //-------------------------------------------------------------------------
982 // Methods called by activity
983 //-------------------------------------------------------------------------
984
985 /**
986 * Save the username and password for a particular host in the WebView's
987 * internal database.
988 * @param host The host that required the credentials.
989 * @param username The username for the given host.
990 * @param password The password for the given host.
991 */
992 public void savePassword(String host, String username, String password) {
993 mDatabase.setUsernamePassword(host, username, password);
994 }
995
996 /**
997 * Set the HTTP authentication credentials for a given host and realm.
998 *
999 * @param host The host for the credentials.
1000 * @param realm The realm for the credentials.
1001 * @param username The username for the password. If it is null, it means
1002 * password can't be saved.
1003 * @param password The password
1004 */
1005 public void setHttpAuthUsernamePassword(String host, String realm,
1006 String username, String password) {
1007 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
1008 }
1009
1010 /**
1011 * Retrieve the HTTP authentication username and password for a given
1012 * host & realm pair
1013 *
1014 * @param host The host for which the credentials apply.
1015 * @param realm The realm for which the credentials apply.
1016 * @return String[] if found, String[0] is username, which can be null and
1017 * String[1] is password. Return null if it can't find anything.
1018 */
1019 public String[] getHttpAuthUsernamePassword(String host, String realm) {
1020 return mDatabase.getHttpAuthUsernamePassword(host, realm);
1021 }
1022
1023 /**
1024 * Destroy the internal state of the WebView. This method should be called
1025 * after the WebView has been removed from the view system. No other
1026 * methods may be called on a WebView after destroy.
1027 */
1028 public void destroy() {
1029 clearTextEntry();
1030 if (mWebViewCore != null) {
1031 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -04001032 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 mCallbackProxy.setWebViewClient(null);
1034 mCallbackProxy.setWebChromeClient(null);
1035 // Tell WebViewCore to destroy itself
1036 WebViewCore webViewCore = mWebViewCore;
1037 mWebViewCore = null; // prevent using partial webViewCore
1038 webViewCore.destroy();
1039 // Remove any pending messages that might not be serviced yet.
1040 mPrivateHandler.removeCallbacksAndMessages(null);
1041 mCallbackProxy.removeCallbacksAndMessages(null);
1042 // Wake up the WebCore thread just in case it is waiting for a
1043 // javascript dialog.
1044 synchronized (mCallbackProxy) {
1045 mCallbackProxy.notify();
1046 }
1047 }
1048 if (mNativeClass != 0) {
1049 nativeDestroy();
1050 mNativeClass = 0;
1051 }
1052 }
1053
1054 /**
1055 * Enables platform notifications of data state and proxy changes.
1056 */
1057 public static void enablePlatformNotifications() {
1058 Network.enablePlatformNotifications();
1059 }
1060
1061 /**
1062 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001063 * from the Activity's onPause() or onStop().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 */
1065 public static void disablePlatformNotifications() {
1066 Network.disablePlatformNotifications();
1067 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001068
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 /**
Feng Qianb3081372009-06-29 15:55:18 -07001070 * Sets JavaScript engine flags.
1071 *
1072 * @param flags JS engine flags in a String
1073 *
1074 * @hide pending API solidification
1075 */
1076 public void setJsFlags(String flags) {
1077 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1078 }
1079
1080 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 * Inform WebView of the network state. This is used to set
1082 * the javascript property window.navigator.isOnline and
1083 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1084 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 */
1086 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001087 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1088 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 }
1090
1091 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001092 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1094 * method no longer stores the display data for this WebView. The previous
1095 * behavior could potentially leak files if {@link #restoreState} was never
1096 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1097 * and restoring the display data.
1098 * @param outState The Bundle to store the WebView state.
1099 * @return The same copy of the back/forward list used to save the state. If
1100 * saveState fails, the returned list will be null.
1101 * @see #savePicture
1102 * @see #restorePicture
1103 */
1104 public WebBackForwardList saveState(Bundle outState) {
1105 if (outState == null) {
1106 return null;
1107 }
1108 // We grab a copy of the back/forward list because a client of WebView
1109 // may have invalidated the history list by calling clearHistory.
1110 WebBackForwardList list = copyBackForwardList();
1111 final int currentIndex = list.getCurrentIndex();
1112 final int size = list.getSize();
1113 // We should fail saving the state if the list is empty or the index is
1114 // not in a valid range.
1115 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1116 return null;
1117 }
1118 outState.putInt("index", currentIndex);
1119 // FIXME: This should just be a byte[][] instead of ArrayList but
1120 // Parcel.java does not have the code to handle multi-dimensional
1121 // arrays.
1122 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1123 for (int i = 0; i < size; i++) {
1124 WebHistoryItem item = list.getItemAtIndex(i);
Cary Clark4fbf81b2009-09-29 16:07:56 -04001125 if (null == item) {
1126 // FIXME: this shouldn't happen
1127 // need to determine how item got set to null
1128 Log.w(LOGTAG, "saveState: Unexpected null history item.");
1129 return null;
1130 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001131 byte[] data = item.getFlattenedData();
1132 if (data == null) {
1133 // It would be very odd to not have any data for a given history
1134 // item. And we will fail to rebuild the history list without
1135 // flattened data.
1136 return null;
1137 }
1138 history.add(data);
1139 }
1140 outState.putSerializable("history", history);
1141 if (mCertificate != null) {
1142 outState.putBundle("certificate",
1143 SslCertificate.saveState(mCertificate));
1144 }
1145 return list;
1146 }
1147
1148 /**
1149 * Save the current display data to the Bundle given. Used in conjunction
1150 * with {@link #saveState}.
1151 * @param b A Bundle to store the display data.
1152 * @param dest The file to store the serialized picture data. Will be
1153 * overwritten with this WebView's picture data.
1154 * @return True if the picture was successfully saved.
1155 */
1156 public boolean savePicture(Bundle b, File dest) {
1157 if (dest == null || b == null) {
1158 return false;
1159 }
1160 final Picture p = capturePicture();
1161 try {
1162 final FileOutputStream out = new FileOutputStream(dest);
1163 p.writeToStream(out);
1164 out.close();
1165 } catch (FileNotFoundException e){
1166 e.printStackTrace();
1167 } catch (IOException e) {
1168 e.printStackTrace();
1169 } catch (RuntimeException e) {
1170 e.printStackTrace();
1171 }
1172 if (dest.length() > 0) {
1173 b.putInt("scrollX", mScrollX);
1174 b.putInt("scrollY", mScrollY);
1175 b.putFloat("scale", mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001176 if (mInZoomOverview) {
1177 b.putFloat("lastScale", mLastScale);
1178 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 return true;
1180 }
1181 return false;
1182 }
1183
1184 /**
1185 * Restore the display data that was save in {@link #savePicture}. Used in
1186 * conjunction with {@link #restoreState}.
1187 * @param b A Bundle containing the saved display data.
1188 * @param src The file where the picture data was stored.
1189 * @return True if the picture was successfully restored.
1190 */
1191 public boolean restorePicture(Bundle b, File src) {
1192 if (src == null || b == null) {
1193 return false;
1194 }
1195 if (src.exists()) {
1196 Picture p = null;
1197 try {
1198 final FileInputStream in = new FileInputStream(src);
1199 p = Picture.createFromStream(in);
1200 in.close();
1201 } catch (FileNotFoundException e){
1202 e.printStackTrace();
1203 } catch (RuntimeException e) {
1204 e.printStackTrace();
1205 } catch (IOException e) {
1206 e.printStackTrace();
1207 }
1208 if (p != null) {
1209 int sx = b.getInt("scrollX", 0);
1210 int sy = b.getInt("scrollY", 0);
1211 float scale = b.getFloat("scale", 1.0f);
1212 mDrawHistory = true;
1213 mHistoryPicture = p;
1214 mScrollX = sx;
1215 mScrollY = sy;
1216 mHistoryWidth = Math.round(p.getWidth() * scale);
1217 mHistoryHeight = Math.round(p.getHeight() * scale);
1218 // as getWidth() / getHeight() of the view are not
1219 // available yet, set up mActualScale, so that when
1220 // onSizeChanged() is called, the rest will be set
1221 // correctly
1222 mActualScale = scale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001223 float lastScale = b.getFloat("lastScale", -1.0f);
1224 if (lastScale > 0) {
1225 mInZoomOverview = true;
1226 mLastScale = lastScale;
1227 } else {
1228 mInZoomOverview = false;
1229 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 invalidate();
1231 return true;
1232 }
1233 }
1234 return false;
1235 }
1236
1237 /**
1238 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001239 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1240 * be called to restore the state of the WebView before using the object. If
1241 * it is called after the WebView has had a chance to build state (load
1242 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 * side-effects. Please note that this method no longer restores the
1244 * display data for this WebView. See {@link #savePicture} and {@link
1245 * #restorePicture} for saving and restoring the display data.
1246 * @param inState The incoming Bundle of state.
1247 * @return The restored back/forward list or null if restoreState failed.
1248 * @see #savePicture
1249 * @see #restorePicture
1250 */
1251 public WebBackForwardList restoreState(Bundle inState) {
1252 WebBackForwardList returnList = null;
1253 if (inState == null) {
1254 return returnList;
1255 }
1256 if (inState.containsKey("index") && inState.containsKey("history")) {
1257 mCertificate = SslCertificate.restoreState(
1258 inState.getBundle("certificate"));
1259
1260 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1261 final int index = inState.getInt("index");
1262 // We can't use a clone of the list because we need to modify the
1263 // shared copy, so synchronize instead to prevent concurrent
1264 // modifications.
1265 synchronized (list) {
1266 final List<byte[]> history =
1267 (List<byte[]>) inState.getSerializable("history");
1268 final int size = history.size();
1269 // Check the index bounds so we don't crash in native code while
1270 // restoring the history index.
1271 if (index < 0 || index >= size) {
1272 return null;
1273 }
1274 for (int i = 0; i < size; i++) {
1275 byte[] data = history.remove(0);
1276 if (data == null) {
1277 // If we somehow have null data, we cannot reconstruct
1278 // the item and thus our history list cannot be rebuilt.
1279 return null;
1280 }
1281 WebHistoryItem item = new WebHistoryItem(data);
1282 list.addHistoryItem(item);
1283 }
1284 // Grab the most recent copy to return to the caller.
1285 returnList = copyBackForwardList();
1286 // Update the copy to have the correct index.
1287 returnList.setCurrentIndex(index);
1288 }
1289 // Remove all pending messages because we are restoring previous
1290 // state.
1291 mWebViewCore.removeMessages();
1292 // Send a restore state message.
1293 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1294 }
1295 return returnList;
1296 }
1297
1298 /**
1299 * Load the given url.
1300 * @param url The url of the resource to load.
1301 */
1302 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001303 if (url == null) {
1304 return;
1305 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 switchOutDrawHistory();
1307 mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
1308 clearTextEntry();
1309 }
1310
1311 /**
Grace Kloba57534302009-05-22 18:55:02 -07001312 * Load the url with postData using "POST" method into the WebView. If url
1313 * is not a network url, it will be loaded with {link
1314 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001315 *
Grace Kloba57534302009-05-22 18:55:02 -07001316 * @param url The url of the resource to load.
1317 * @param postData The data will be passed to "POST" request.
Grace Kloba57534302009-05-22 18:55:02 -07001318 */
1319 public void postUrl(String url, byte[] postData) {
1320 if (URLUtil.isNetworkUrl(url)) {
1321 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001322 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1323 arg.mUrl = url;
1324 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001325 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
1326 clearTextEntry();
1327 } else {
1328 loadUrl(url);
1329 }
1330 }
1331
1332 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 * Load the given data into the WebView. This will load the data into
1334 * WebView using the data: scheme. Content loaded through this mechanism
1335 * does not have the ability to load content from the network.
1336 * @param data A String of data in the given encoding.
1337 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1338 * @param encoding The encoding of the data. i.e. utf-8, base64
1339 */
1340 public void loadData(String data, String mimeType, String encoding) {
1341 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1342 }
1343
1344 /**
1345 * Load the given data into the WebView, use the provided URL as the base
1346 * URL for the content. The base URL is the URL that represents the page
1347 * that is loaded through this interface. As such, it is used for the
1348 * history entry and to resolve any relative URLs. The failUrl is used if
1349 * browser fails to load the data provided. If it is empty or null, and the
1350 * load fails, then no history entry is created.
1351 * <p>
1352 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1353 * files through "file:///android_asset/" for the sub resources is more
1354 * restricted. If you provide null or empty string as baseUrl, you won't be
1355 * able to access asset files. If the baseUrl is anything other than
1356 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1357 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001358 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 * @param baseUrl Url to resolve relative paths with, if null defaults to
1360 * "about:blank"
1361 * @param data A String of data in the given encoding.
1362 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1363 * defaults to "text/html"
1364 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
1365 * @param failUrl URL to use if the content fails to load or null.
1366 */
1367 public void loadDataWithBaseURL(String baseUrl, String data,
1368 String mimeType, String encoding, String failUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1371 loadData(data, mimeType, encoding);
1372 return;
1373 }
1374 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001375 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1376 arg.mBaseUrl = baseUrl;
1377 arg.mData = data;
1378 arg.mMimeType = mimeType;
1379 arg.mEncoding = encoding;
1380 arg.mFailUrl = failUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
1382 clearTextEntry();
1383 }
1384
1385 /**
1386 * Stop the current load.
1387 */
1388 public void stopLoading() {
1389 // TODO: should we clear all the messages in the queue before sending
1390 // STOP_LOADING?
1391 switchOutDrawHistory();
1392 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1393 }
1394
1395 /**
1396 * Reload the current url.
1397 */
1398 public void reload() {
Leon Scroggins74077c82009-09-14 18:56:48 -04001399 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 switchOutDrawHistory();
1401 mWebViewCore.sendMessage(EventHub.RELOAD);
1402 }
1403
1404 /**
1405 * Return true if this WebView has a back history item.
1406 * @return True iff this WebView has a back history item.
1407 */
1408 public boolean canGoBack() {
1409 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1410 synchronized (l) {
1411 if (l.getClearPending()) {
1412 return false;
1413 } else {
1414 return l.getCurrentIndex() > 0;
1415 }
1416 }
1417 }
1418
1419 /**
1420 * Go back in the history of this WebView.
1421 */
1422 public void goBack() {
1423 goBackOrForward(-1);
1424 }
1425
1426 /**
1427 * Return true if this WebView has a forward history item.
1428 * @return True iff this Webview has a forward history item.
1429 */
1430 public boolean canGoForward() {
1431 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1432 synchronized (l) {
1433 if (l.getClearPending()) {
1434 return false;
1435 } else {
1436 return l.getCurrentIndex() < l.getSize() - 1;
1437 }
1438 }
1439 }
1440
1441 /**
1442 * Go forward in the history of this WebView.
1443 */
1444 public void goForward() {
1445 goBackOrForward(1);
1446 }
1447
1448 /**
1449 * Return true if the page can go back or forward the given
1450 * number of steps.
1451 * @param steps The negative or positive number of steps to move the
1452 * history.
1453 */
1454 public boolean canGoBackOrForward(int steps) {
1455 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1456 synchronized (l) {
1457 if (l.getClearPending()) {
1458 return false;
1459 } else {
1460 int newIndex = l.getCurrentIndex() + steps;
1461 return newIndex >= 0 && newIndex < l.getSize();
1462 }
1463 }
1464 }
1465
1466 /**
1467 * Go to the history item that is the number of steps away from
1468 * the current item. Steps is negative if backward and positive
1469 * if forward.
1470 * @param steps The number of steps to take back or forward in the back
1471 * forward list.
1472 */
1473 public void goBackOrForward(int steps) {
1474 goBackOrForward(steps, false);
1475 }
1476
1477 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
1478 // every time we go back or forward, we want to reset the
1479 // WebView certificate:
1480 // if the new site is secure, we will reload it and get a
1481 // new certificate set;
1482 // if the new site is not secure, the certificate must be
1483 // null, and that will be the case
1484 mCertificate = null;
1485 if (steps != 0) {
1486 clearTextEntry();
1487 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
1488 ignoreSnapshot ? 1 : 0);
1489 }
1490 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001491
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001492 private boolean extendScroll(int y) {
1493 int finalY = mScroller.getFinalY();
1494 int newY = pinLocY(finalY + y);
1495 if (newY == finalY) return false;
1496 mScroller.setFinalY(newY);
1497 mScroller.extendDuration(computeDuration(0, y));
1498 return true;
1499 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001500
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 /**
1502 * Scroll the contents of the view up by half the view size
1503 * @param top true to jump to the top of the page
1504 * @return true if the page was scrolled
1505 */
1506 public boolean pageUp(boolean top) {
1507 if (mNativeClass == 0) {
1508 return false;
1509 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001510 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 if (top) {
1512 // go to the top of the document
1513 return pinScrollTo(mScrollX, 0, true, 0);
1514 }
1515 // Page up
1516 int h = getHeight();
1517 int y;
1518 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1519 y = -h + PAGE_SCROLL_OVERLAP;
1520 } else {
1521 y = -h / 2;
1522 }
1523 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001524 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 : extendScroll(y);
1526 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001527
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001528 /**
1529 * Scroll the contents of the view down by half the page size
1530 * @param bottom true to jump to bottom of page
1531 * @return true if the page was scrolled
1532 */
1533 public boolean pageDown(boolean bottom) {
1534 if (mNativeClass == 0) {
1535 return false;
1536 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001537 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001538 if (bottom) {
1539 return pinScrollTo(mScrollX, mContentHeight, true, 0);
1540 }
1541 // Page down.
1542 int h = getHeight();
1543 int y;
1544 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1545 y = h - PAGE_SCROLL_OVERLAP;
1546 } else {
1547 y = h / 2;
1548 }
1549 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001550 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001551 : extendScroll(y);
1552 }
1553
1554 /**
1555 * Clear the view so that onDraw() will draw nothing but white background,
1556 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
1557 */
1558 public void clearView() {
1559 mContentWidth = 0;
1560 mContentHeight = 0;
1561 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
1562 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 /**
1565 * Return a new picture that captures the current display of the webview.
1566 * This is a copy of the display, and will be unaffected if the webview
1567 * later loads a different URL.
1568 *
1569 * @return a picture containing the current contents of the view. Note this
1570 * picture is of the entire document, and is not restricted to the
1571 * bounds of the view.
1572 */
1573 public Picture capturePicture() {
Cary Clarkd6982c92009-05-29 11:02:22 -04001574 if (null == mWebViewCore) return null; // check for out of memory tab
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001575 return mWebViewCore.copyContentPicture();
1576 }
1577
1578 /**
1579 * Return true if the browser is displaying a TextView for text input.
1580 */
1581 private boolean inEditingMode() {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001582 return mWebTextView != null && mWebTextView.getParent() != null
1583 && mWebTextView.hasFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584 }
1585
1586 private void clearTextEntry() {
1587 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001588 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001589 }
1590 }
1591
Cary Clarkd6982c92009-05-29 11:02:22 -04001592 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001593 * Return the current scale of the WebView
1594 * @return The current scale.
1595 */
1596 public float getScale() {
1597 return mActualScale;
1598 }
1599
1600 /**
1601 * Set the initial scale for the WebView. 0 means default. If
1602 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
1603 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
1604 * WebView starts will this value as initial scale.
1605 *
1606 * @param scaleInPercent The initial scale in percent.
1607 */
1608 public void setInitialScale(int scaleInPercent) {
1609 mInitialScale = scaleInPercent;
1610 }
1611
1612 /**
1613 * Invoke the graphical zoom picker widget for this WebView. This will
1614 * result in the zoom widget appearing on the screen to control the zoom
1615 * level of this WebView.
1616 */
1617 public void invokeZoomPicker() {
1618 if (!getSettings().supportZoom()) {
1619 Log.w(LOGTAG, "This WebView doesn't support zoom.");
1620 return;
1621 }
1622 clearTextEntry();
The Android Open Source Project10592532009-03-18 17:39:46 -07001623 if (getSettings().getBuiltInZoomControls()) {
1624 mZoomButtonsController.setVisible(true);
1625 } else {
1626 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
1627 mPrivateHandler.postDelayed(mZoomControlRunnable,
1628 ZOOM_CONTROLS_TIMEOUT);
1629 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 }
1631
1632 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001633 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 * is found and the anchor has a non-javascript url, the HitTestResult type
1635 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
1636 * anchor does not have a url or if it is a javascript url, the type will
1637 * be UNKNOWN_TYPE and the url has to be retrieved through
1638 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
1639 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
1640 * the "extra" field. A type of
1641 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
1642 * a child node. If a phone number is found, the HitTestResult type is set
1643 * to PHONE_TYPE and the phone number is set in the "extra" field of
1644 * HitTestResult. If a map address is found, the HitTestResult type is set
1645 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
1646 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
1647 * and the email is set in the "extra" field of HitTestResult. Otherwise,
1648 * HitTestResult type is set to UNKNOWN_TYPE.
1649 */
1650 public HitTestResult getHitTestResult() {
1651 if (mNativeClass == 0) {
1652 return null;
1653 }
1654
1655 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04001656 if (nativeHasCursorNode()) {
1657 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04001659 } else {
1660 String text = nativeCursorText();
1661 if (text != null) {
1662 if (text.startsWith(SCHEME_TEL)) {
1663 result.setType(HitTestResult.PHONE_TYPE);
1664 result.setExtra(text.substring(SCHEME_TEL.length()));
1665 } else if (text.startsWith(SCHEME_MAILTO)) {
1666 result.setType(HitTestResult.EMAIL_TYPE);
1667 result.setExtra(text.substring(SCHEME_MAILTO.length()));
1668 } else if (text.startsWith(SCHEME_GEO)) {
1669 result.setType(HitTestResult.GEO_TYPE);
1670 result.setExtra(URLDecoder.decode(text
1671 .substring(SCHEME_GEO.length())));
1672 } else if (nativeCursorIsAnchor()) {
1673 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
1674 result.setExtra(text);
1675 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 }
1677 }
1678 }
1679 int type = result.getType();
1680 if (type == HitTestResult.UNKNOWN_TYPE
1681 || type == HitTestResult.SRC_ANCHOR_TYPE) {
1682 // Now check to see if it is an image.
Leon Scroggins0236e672009-09-02 21:12:08 -04001683 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1684 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 String text = nativeImageURI(contentX, contentY);
1686 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001687 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
1688 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001689 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
1690 result.setExtra(text);
1691 }
1692 }
1693 return result;
1694 }
1695
1696 /**
1697 * Request the href of an anchor element due to getFocusNodePath returning
1698 * "href." If hrefMsg is null, this method returns immediately and does not
1699 * dispatch hrefMsg to its target.
Cary Clarkd6982c92009-05-29 11:02:22 -04001700 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 * @param hrefMsg This message will be dispatched with the result of the
1702 * request as the data member with "url" as key. The result can
1703 * be null.
1704 */
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001705 // FIXME: API change required to change the name of this function. We now
1706 // look at the cursor node, and not the focus node. Also, what is
1707 // getFocusNodePath?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001708 public void requestFocusNodeHref(Message hrefMsg) {
1709 if (hrefMsg == null || mNativeClass == 0) {
1710 return;
1711 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001712 if (nativeCursorIsAnchor()) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001713 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
Cary Clarkd6982c92009-05-29 11:02:22 -04001714 nativeCursorFramePointer(), nativeCursorNodePointer(),
1715 hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 }
1717 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001719 /**
1720 * Request the url of the image last touched by the user. msg will be sent
1721 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04001722 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001723 * @param msg This message will be dispatched with the result of the request
1724 * as the data member with "url" as key. The result can be null.
1725 */
1726 public void requestImageRef(Message msg) {
Cary Clark7f970112009-10-15 15:29:08 -04001727 if (0 == mNativeClass) return; // client isn't initialized
Leon Scroggins0236e672009-09-02 21:12:08 -04001728 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1729 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 String ref = nativeImageURI(contentX, contentY);
1731 Bundle data = msg.getData();
1732 data.putString("url", ref);
1733 msg.setData(data);
1734 msg.sendToTarget();
1735 }
1736
1737 private static int pinLoc(int x, int viewMax, int docMax) {
1738// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
1739 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001740 // pin the short document to the top/left of the screen
1741 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001742// Log.d(LOGTAG, "--- center " + x);
1743 } else if (x < 0) {
1744 x = 0;
1745// Log.d(LOGTAG, "--- zero");
1746 } else if (x + viewMax > docMax) {
1747 x = docMax - viewMax;
1748// Log.d(LOGTAG, "--- pin " + x);
1749 }
1750 return x;
1751 }
1752
1753 // Expects x in view coordinates
1754 private int pinLocX(int x) {
1755 return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
1756 }
1757
1758 // Expects y in view coordinates
1759 private int pinLocY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04001760 int titleH = getTitleHeight();
1761 // if the titlebar is still visible, just pin against 0
1762 if (y <= titleH) {
1763 return Math.max(y, 0);
1764 }
1765 // convert to 0-based coordinate (subtract the title height)
1766 // pin(), and then add the title height back in
1767 return pinLoc(y - titleH, getViewHeight(),
1768 computeVerticalScrollRange()) + titleH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001769 }
1770
Leon Scroggins0236e672009-09-02 21:12:08 -04001771 /**
1772 * A title bar which is embedded in this WebView, and scrolls along with it
1773 * vertically, but not horizontally.
1774 */
1775 private View mTitleBar;
1776
1777 /**
Leon Scroggins58992ea2009-09-17 15:55:31 -04001778 * Since we draw the title bar ourselves, we removed the shadow from the
1779 * browser's activity. We do want a shadow at the bottom of the title bar,
1780 * or at the top of the screen if the title bar is not visible. This
1781 * drawable serves that purpose.
1782 */
1783 private Drawable mTitleShadow;
1784
1785 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001786 * Add or remove a title bar to be embedded into the WebView, and scroll
1787 * along with it vertically, while remaining in view horizontally. Pass
1788 * null to remove the title bar from the WebView, and return to drawing
1789 * the WebView normally without translating to account for the title bar.
1790 * @hide
1791 */
Leon Scroggins078c52c2009-09-04 16:58:09 -04001792 public void setEmbeddedTitleBar(View v) {
1793 if (mTitleBar == v) return;
1794 if (mTitleBar != null) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001795 removeView(mTitleBar);
Leon Scroggins078c52c2009-09-04 16:58:09 -04001796 }
1797 if (null != v) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001798 addView(v, new AbsoluteLayout.LayoutParams(
1799 ViewGroup.LayoutParams.FILL_PARENT,
Leon Scroggins078c52c2009-09-04 16:58:09 -04001800 ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
Leon Scroggins58992ea2009-09-17 15:55:31 -04001801 if (mTitleShadow == null) {
1802 mTitleShadow = (Drawable) mContext.getResources().getDrawable(
1803 com.android.internal.R.drawable.title_bar_shadow);
1804 }
Leon Scroggins0236e672009-09-02 21:12:08 -04001805 }
1806 mTitleBar = v;
1807 }
1808
1809 /**
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001810 * Given a distance in view space, convert it to content space. Note: this
1811 * does not reflect translation, just scaling, so this should not be called
1812 * with coordinates, but should be called for dimensions like width or
1813 * height.
1814 */
1815 private int viewToContentDimension(int d) {
1816 return Math.round(d * mInvActualScale);
1817 }
1818
1819 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001820 * Given an x coordinate in view space, convert it to content space. Also
1821 * may be used for absolute heights (such as for the WebTextView's
1822 * textSize, which is unaffected by the height of the title bar).
1823 */
1824 /*package*/ int viewToContentX(int x) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001825 return viewToContentDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826 }
1827
Leon Scroggins0236e672009-09-02 21:12:08 -04001828 /**
1829 * Given a y coordinate in view space, convert it to content space.
1830 * Takes into account the height of the title bar if there is one
1831 * embedded into the WebView.
1832 */
1833 /*package*/ int viewToContentY(int y) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001834 return viewToContentDimension(y - getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04001835 }
1836
1837 /**
1838 * Given a distance in content space, convert it to view space. Note: this
1839 * does not reflect translation, just scaling, so this should not be called
1840 * with coordinates, but should be called for dimensions like width or
1841 * height.
1842 */
1843 /*package*/ int contentToViewDimension(int d) {
1844 return Math.round(d * mActualScale);
Leon Scroggins0236e672009-09-02 21:12:08 -04001845 }
1846
1847 /**
1848 * Given an x coordinate in content space, convert it to view
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001849 * space.
Leon Scroggins0236e672009-09-02 21:12:08 -04001850 */
1851 /*package*/ int contentToViewX(int x) {
Mike Reede8853fc2009-09-04 14:01:48 -04001852 return contentToViewDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 }
1854
Leon Scroggins0236e672009-09-02 21:12:08 -04001855 /**
1856 * Given a y coordinate in content space, convert it to view
1857 * space. Takes into account the height of the title bar.
1858 */
1859 /*package*/ int contentToViewY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04001860 return contentToViewDimension(y) + getTitleHeight();
Leon Scroggins0236e672009-09-02 21:12:08 -04001861 }
1862
Mike Reede9e86b82009-09-15 11:26:53 -04001863 private Rect contentToViewRect(Rect x) {
1864 return new Rect(contentToViewX(x.left), contentToViewY(x.top),
1865 contentToViewX(x.right), contentToViewY(x.bottom));
1866 }
1867
1868 /* To invalidate a rectangle in content coordinates, we need to transform
1869 the rect into view coordinates, so we can then call invalidate(...).
1870
1871 Normally, we would just call contentToView[XY](...), which eventually
1872 calls Math.round(coordinate * mActualScale). However, for invalidates,
1873 we need to account for the slop that occurs with antialiasing. To
1874 address that, we are a little more liberal in the size of the rect that
1875 we invalidate.
1876
1877 This liberal calculation calls floor() for the top/left, and ceil() for
1878 the bottom/right coordinates. This catches the possible extra pixels of
1879 antialiasing that we might have missed with just round().
1880 */
1881
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001882 // Called by JNI to invalidate the View, given rectangle coordinates in
1883 // content space
1884 private void viewInvalidate(int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04001885 final float scale = mActualScale;
1886 final int dy = getTitleHeight();
1887 invalidate((int)Math.floor(l * scale),
1888 (int)Math.floor(t * scale) + dy,
1889 (int)Math.ceil(r * scale),
1890 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001891 }
1892
1893 // Called by JNI to invalidate the View after a delay, given rectangle
1894 // coordinates in content space
1895 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04001896 final float scale = mActualScale;
1897 final int dy = getTitleHeight();
1898 postInvalidateDelayed(delay,
1899 (int)Math.floor(l * scale),
1900 (int)Math.floor(t * scale) + dy,
1901 (int)Math.ceil(r * scale),
1902 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 }
1904
Mike Reede9e86b82009-09-15 11:26:53 -04001905 private void invalidateContentRect(Rect r) {
1906 viewInvalidate(r.left, r.top, r.right, r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001907 }
1908
Cary Clark278ce052009-08-31 16:08:42 -04001909 // stop the scroll animation, and don't let a subsequent fling add
1910 // to the existing velocity
1911 private void abortAnimation() {
1912 mScroller.abortAnimation();
1913 mLastVelocity = 0;
1914 }
1915
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001916 /* call from webcoreview.draw(), so we're still executing in the UI thread
1917 */
1918 private void recordNewContentSize(int w, int h, boolean updateLayout) {
1919
1920 // premature data from webkit, ignore
1921 if ((w | h) == 0) {
1922 return;
1923 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001924
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001925 // don't abort a scroll animation if we didn't change anything
1926 if (mContentWidth != w || mContentHeight != h) {
1927 // record new dimensions
1928 mContentWidth = w;
1929 mContentHeight = h;
1930 // If history Picture is drawn, don't update scroll. They will be
1931 // updated when we get out of that mode.
1932 if (!mDrawHistory) {
1933 // repin our scroll, taking into account the new content size
1934 int oldX = mScrollX;
1935 int oldY = mScrollY;
1936 mScrollX = pinLocX(mScrollX);
1937 mScrollY = pinLocY(mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001938 if (oldX != mScrollX || oldY != mScrollY) {
1939 sendOurVisibleRect();
1940 }
Leon Scrogginsd84e7d52009-09-29 11:11:45 -04001941 if (!mScroller.isFinished()) {
1942 // We are in the middle of a scroll. Repin the final scroll
1943 // position.
1944 mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
1945 mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
1946 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001947 }
1948 }
1949 contentSizeChanged(updateLayout);
1950 }
1951
1952 private void setNewZoomScale(float scale, boolean force) {
1953 if (scale < mMinZoomScale) {
1954 scale = mMinZoomScale;
1955 } else if (scale > mMaxZoomScale) {
1956 scale = mMaxZoomScale;
1957 }
1958 if (scale != mActualScale || force) {
1959 if (mDrawHistory) {
1960 // If history Picture is drawn, don't update scroll. They will
1961 // be updated when we get out of that mode.
1962 if (scale != mActualScale && !mPreviewZoomOnly) {
1963 mCallbackProxy.onScaleChanged(mActualScale, scale);
1964 }
1965 mActualScale = scale;
1966 mInvActualScale = 1 / scale;
1967 if (!mPreviewZoomOnly) {
1968 sendViewSizeZoom();
1969 }
1970 } else {
1971 // update our scroll so we don't appear to jump
1972 // i.e. keep the center of the doc in the center of the view
1973
1974 int oldX = mScrollX;
1975 int oldY = mScrollY;
1976 float ratio = scale * mInvActualScale; // old inverse
1977 float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
Grace Klobac0c03af2009-09-17 11:01:44 -07001978 float sy = ratio * oldY + (ratio - 1)
1979 * (mZoomCenterY - getTitleHeight());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980
1981 // now update our new scale and inverse
1982 if (scale != mActualScale && !mPreviewZoomOnly) {
1983 mCallbackProxy.onScaleChanged(mActualScale, scale);
1984 }
1985 mActualScale = scale;
1986 mInvActualScale = 1 / scale;
1987
Patrick Scott0a5ce012009-07-02 08:56:10 -04001988 // Scale all the child views
1989 mViewManager.scaleAll();
1990
Cary Clarkd6982c92009-05-29 11:02:22 -04001991 // as we don't have animation for scaling, don't do animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001992 // for scrolling, as it causes weird intermediate state
1993 // pinScrollTo(Math.round(sx), Math.round(sy));
1994 mScrollX = pinLocX(Math.round(sx));
1995 mScrollY = pinLocY(Math.round(sy));
1996
1997 if (!mPreviewZoomOnly) {
1998 sendViewSizeZoom();
1999 sendOurVisibleRect();
2000 }
2001 }
2002 }
2003 }
2004
2005 // Used to avoid sending many visible rect messages.
2006 private Rect mLastVisibleRectSent;
2007 private Rect mLastGlobalRect;
2008
2009 private Rect sendOurVisibleRect() {
2010 Rect rect = new Rect();
2011 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002012 // Rect.equals() checks for null input.
2013 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04002014 Point pos = new Point(rect.left, rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002015 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Cary Clarked56eda2009-06-18 09:48:47 -04002016 nativeMoveGeneration(), 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002017 mLastVisibleRectSent = rect;
2018 }
2019 Rect globalRect = new Rect();
2020 if (getGlobalVisibleRect(globalRect)
2021 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04002022 if (DebugFlags.WEB_VIEW) {
2023 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
2024 + globalRect.top + ",r=" + globalRect.right + ",b="
2025 + globalRect.bottom);
2026 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002027 // TODO: the global offset is only used by windowRect()
2028 // in ChromeClientAndroid ; other clients such as touch
2029 // and mouse events could return view + screen relative points.
2030 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
2031 mLastGlobalRect = globalRect;
2032 }
2033 return rect;
2034 }
2035
2036 // Sets r to be the visible rectangle of our webview in view coordinates
2037 private void calcOurVisibleRect(Rect r) {
2038 Point p = new Point();
2039 getGlobalVisibleRect(r, p);
2040 r.offset(-p.x, -p.y);
Cary Clark3524be92009-06-22 13:09:11 -04002041 if (mFindIsUp) {
Cary Clark5bb6b522009-09-21 11:58:31 -04002042 r.bottom -= mFindHeight;
Cary Clark3524be92009-06-22 13:09:11 -04002043 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002044 }
2045
2046 // Sets r to be our visible rectangle in content coordinates
2047 private void calcOurContentVisibleRect(Rect r) {
2048 calcOurVisibleRect(r);
Leon Scroggins0236e672009-09-02 21:12:08 -04002049 r.left = viewToContentX(r.left);
Leon Scroggins37df6a82009-09-23 10:31:23 -04002050 // viewToContentY will remove the total height of the title bar. Add
2051 // the visible height back in to account for the fact that if the title
2052 // bar is partially visible, the part of the visible rect which is
2053 // displaying our content is displaced by that amount.
Grace Kloba8eff73f2009-09-24 09:34:32 -07002054 r.top = viewToContentY(r.top + getVisibleTitleHeight());
Leon Scroggins0236e672009-09-02 21:12:08 -04002055 r.right = viewToContentX(r.right);
Grace Kloba8eff73f2009-09-24 09:34:32 -07002056 r.bottom = viewToContentY(r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002057 }
2058
Grace Klobaef347ef2009-07-30 11:20:32 -07002059 static class ViewSizeData {
2060 int mWidth;
2061 int mHeight;
2062 int mTextWrapWidth;
2063 float mScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002064 boolean mIgnoreHeight;
Grace Klobaef347ef2009-07-30 11:20:32 -07002065 }
2066
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002067 /**
2068 * Compute unzoomed width and height, and if they differ from the last
2069 * values we sent, send them to webkit (to be used has new viewport)
2070 *
2071 * @return true if new values were sent
2072 */
2073 private boolean sendViewSizeZoom() {
Grace Klobaef347ef2009-07-30 11:20:32 -07002074 int viewWidth = getViewWidth();
2075 int newWidth = Math.round(viewWidth * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002076 int newHeight = Math.round(getViewHeight() * mInvActualScale);
2077 /*
2078 * Because the native side may have already done a layout before the
2079 * View system was able to measure us, we have to send a height of 0 to
2080 * remove excess whitespace when we grow our width. This will trigger a
2081 * layout and a change in content size. This content size change will
2082 * mean that contentSizeChanged will either call this method directly or
2083 * indirectly from onSizeChanged.
2084 */
2085 if (newWidth > mLastWidthSent && mWrapContent) {
2086 newHeight = 0;
2087 }
2088 // Avoid sending another message if the dimensions have not changed.
2089 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
Grace Klobaef347ef2009-07-30 11:20:32 -07002090 ViewSizeData data = new ViewSizeData();
2091 data.mWidth = newWidth;
2092 data.mHeight = newHeight;
2093 // while in zoom overview mode, the text are wrapped to the screen
2094 // width matching mLastScale. So that we don't trigger re-flow while
2095 // toggling between overview mode and normal mode.
2096 data.mTextWrapWidth = mInZoomOverview ? Math.round(viewWidth
2097 / mLastScale) : newWidth;
2098 data.mScale = mActualScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002099 data.mIgnoreHeight = mZoomScale != 0 && !mHeightCanMeasure;
Grace Klobaef347ef2009-07-30 11:20:32 -07002100 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002101 mLastWidthSent = newWidth;
2102 mLastHeightSent = newHeight;
2103 return true;
2104 }
2105 return false;
2106 }
2107
2108 @Override
2109 protected int computeHorizontalScrollRange() {
2110 if (mDrawHistory) {
2111 return mHistoryWidth;
2112 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002113 // to avoid rounding error caused unnecessary scrollbar, use floor
2114 return (int) Math.floor(mContentWidth * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002115 }
2116 }
2117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002118 @Override
2119 protected int computeVerticalScrollRange() {
2120 if (mDrawHistory) {
2121 return mHistoryHeight;
2122 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002123 // to avoid rounding error caused unnecessary scrollbar, use floor
2124 return (int) Math.floor(mContentHeight * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002125 }
2126 }
2127
Leon Scroggins0236e672009-09-02 21:12:08 -04002128 @Override
2129 protected int computeVerticalScrollOffset() {
Mike Reede8853fc2009-09-04 14:01:48 -04002130 return Math.max(mScrollY - getTitleHeight(), 0);
Leon Scroggins0236e672009-09-02 21:12:08 -04002131 }
2132
2133 @Override
2134 protected int computeVerticalScrollExtent() {
2135 return getViewHeight();
2136 }
2137
Mike Reede8853fc2009-09-04 14:01:48 -04002138 /** @hide */
2139 @Override
2140 protected void onDrawVerticalScrollBar(Canvas canvas,
2141 Drawable scrollBar,
2142 int l, int t, int r, int b) {
2143 scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
2144 scrollBar.draw(canvas);
2145 }
2146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002147 /**
2148 * Get the url for the current page. This is not always the same as the url
2149 * passed to WebViewClient.onPageStarted because although the load for
2150 * that url has begun, the current page may not have changed.
2151 * @return The url for the current page.
2152 */
2153 public String getUrl() {
2154 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2155 return h != null ? h.getUrl() : null;
2156 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002157
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002158 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002159 * Get the original url for the current page. This is not always the same
2160 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002161 * load for that url has begun, the current page may not have changed.
2162 * Also, there may have been redirects resulting in a different url to that
2163 * originally requested.
2164 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002165 */
2166 public String getOriginalUrl() {
2167 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2168 return h != null ? h.getOriginalUrl() : null;
2169 }
2170
2171 /**
2172 * Get the title for the current page. This is the title of the current page
2173 * until WebViewClient.onReceivedTitle is called.
2174 * @return The title for the current page.
2175 */
2176 public String getTitle() {
2177 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2178 return h != null ? h.getTitle() : null;
2179 }
2180
2181 /**
2182 * Get the favicon for the current page. This is the favicon of the current
2183 * page until WebViewClient.onReceivedIcon is called.
2184 * @return The favicon for the current page.
2185 */
2186 public Bitmap getFavicon() {
2187 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2188 return h != null ? h.getFavicon() : null;
2189 }
2190
2191 /**
Patrick Scott2ba12622009-08-04 13:20:05 -04002192 * Get the touch icon url for the apple-touch-icon <link> element.
2193 * @hide
2194 */
2195 public String getTouchIconUrl() {
2196 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2197 return h != null ? h.getTouchIconUrl() : null;
2198 }
2199
2200 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002201 * Get the progress for the current page.
2202 * @return The progress for the current page between 0 and 100.
2203 */
2204 public int getProgress() {
2205 return mCallbackProxy.getProgress();
2206 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002207
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002208 /**
2209 * @return the height of the HTML content.
2210 */
2211 public int getContentHeight() {
2212 return mContentHeight;
2213 }
2214
2215 /**
Leon Scrogginsea96d1e2009-09-23 13:41:01 -04002216 * @return the width of the HTML content.
2217 * @hide
2218 */
2219 public int getContentWidth() {
2220 return mContentWidth;
2221 }
2222
2223 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002224 * Pause all layout, parsing, and javascript timers for all webviews. This
2225 * is a global requests, not restricted to just this webview. This can be
2226 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002227 */
2228 public void pauseTimers() {
2229 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
2230 }
2231
2232 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002233 * Resume all layout, parsing, and javascript timers for all webviews.
2234 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002235 */
2236 public void resumeTimers() {
2237 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
2238 }
2239
2240 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002241 * Call this to pause any extra processing associated with this view and
2242 * its associated DOM/plugins/javascript/etc. For example, if the view is
2243 * taken offscreen, this could be called to reduce unnecessary CPU and/or
2244 * network traffic. When the view is again "active", call onResume().
2245 *
2246 * Note that this differs from pauseTimers(), which affects all views/DOMs
2247 * @hide
2248 */
2249 public void onPause() {
2250 if (!mIsPaused) {
2251 mIsPaused = true;
2252 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
2253 }
2254 }
2255
2256 /**
2257 * Call this to balanace a previous call to onPause()
2258 * @hide
2259 */
2260 public void onResume() {
2261 if (mIsPaused) {
2262 mIsPaused = false;
2263 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2264 }
2265 }
2266
2267 /**
2268 * Returns true if the view is paused, meaning onPause() was called. Calling
2269 * onResume() sets the paused state back to false.
2270 * @hide
2271 */
2272 public boolean isPaused() {
2273 return mIsPaused;
2274 }
2275
2276 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002277 * Call this to inform the view that memory is low so that it can
2278 * free any available memory.
2279 * @hide
2280 */
2281 public void freeMemory() {
2282 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2283 }
2284
2285 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002286 * Clear the resource cache. Note that the cache is per-application, so
2287 * this will clear the cache for all WebViews used.
2288 *
2289 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002290 */
2291 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002292 // Note: this really needs to be a static method as it clears cache for all
2293 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2294 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002295 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2296 includeDiskFiles ? 1 : 0, 0);
2297 }
2298
2299 /**
2300 * Make sure that clearing the form data removes the adapter from the
2301 * currently focused textfield if there is one.
2302 */
2303 public void clearFormData() {
2304 if (inEditingMode()) {
2305 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002306 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002307 }
2308 }
2309
2310 /**
2311 * Tell the WebView to clear its internal back/forward list.
2312 */
2313 public void clearHistory() {
2314 mCallbackProxy.getBackForwardList().setClearPending();
2315 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2316 }
2317
2318 /**
2319 * Clear the SSL preferences table stored in response to proceeding with SSL
2320 * certificate errors.
2321 */
2322 public void clearSslPreferences() {
2323 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2324 }
2325
2326 /**
2327 * Return the WebBackForwardList for this WebView. This contains the
2328 * back/forward list for use in querying each item in the history stack.
2329 * This is a copy of the private WebBackForwardList so it contains only a
2330 * snapshot of the current state. Multiple calls to this method may return
2331 * different objects. The object returned from this method will not be
2332 * updated to reflect any new state.
2333 */
2334 public WebBackForwardList copyBackForwardList() {
2335 return mCallbackProxy.getBackForwardList().clone();
2336 }
2337
2338 /*
2339 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04002340 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002341 * calling findAll.
2342 *
2343 * @param forward Direction to search.
2344 */
2345 public void findNext(boolean forward) {
Cary Clark7f970112009-10-15 15:29:08 -04002346 if (0 == mNativeClass) return; // client isn't initialized
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002347 nativeFindNext(forward);
2348 }
2349
2350 /*
2351 * Find all instances of find on the page and highlight them.
2352 * @param find String to find.
2353 * @return int The number of occurances of the String "find"
2354 * that were found.
2355 */
2356 public int findAll(String find) {
Cary Clark7f970112009-10-15 15:29:08 -04002357 if (0 == mNativeClass) return 0; // client isn't initialized
Cary Clark5bb6b522009-09-21 11:58:31 -04002358 if (mFindIsUp == false) {
2359 recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
2360 false);
2361 mFindIsUp = true;
2362 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002363 int result = nativeFindAll(find.toLowerCase(), find.toUpperCase());
2364 invalidate();
2365 return result;
2366 }
2367
2368 // Used to know whether the find dialog is open. Affects whether
2369 // or not we draw the highlights for matches.
2370 private boolean mFindIsUp;
Cary Clark5bb6b522009-09-21 11:58:31 -04002371 private int mFindHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002373 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002374 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002375 * location. Currently, only addresses in the United States are detected,
2376 * and consist of:
2377 * - a house number
2378 * - a street name
2379 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2380 * - a city name
2381 * - a state or territory, either spelled out or two-letter abbr.
2382 * - an optional 5 digit or 9 digit zip code.
2383 *
2384 * All names must be correctly capitalized, and the zip code, if present,
2385 * must be valid for the state. The street type must be a standard USPS
2386 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04002387 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002388 * five digits.
2389 * @param addr The string to search for addresses.
2390 *
2391 * @return the address, or if no address is found, return null.
2392 */
2393 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04002394 return findAddress(addr, false);
2395 }
2396
2397 /**
2398 * @hide
2399 * Return the first substring consisting of the address of a physical
2400 * location. Currently, only addresses in the United States are detected,
2401 * and consist of:
2402 * - a house number
2403 * - a street name
2404 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2405 * - a city name
2406 * - a state or territory, either spelled out or two-letter abbr.
2407 * - an optional 5 digit or 9 digit zip code.
2408 *
2409 * Names are optionally capitalized, and the zip code, if present,
2410 * must be valid for the state. The street type must be a standard USPS
2411 * spelling or abbreviation. The state or territory must also be spelled
2412 * or abbreviated using USPS standards. The house number may not exceed
2413 * five digits.
2414 * @param addr The string to search for addresses.
2415 * @param caseInsensitive addr Set to true to make search ignore case.
2416 *
2417 * @return the address, or if no address is found, return null.
2418 */
2419 public static String findAddress(String addr, boolean caseInsensitive) {
2420 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002421 }
2422
2423 /*
2424 * Clear the highlighting surrounding text matches created by findAll.
2425 */
2426 public void clearMatches() {
Cary Clark5bb6b522009-09-21 11:58:31 -04002427 if (mFindIsUp) {
2428 recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
2429 false);
2430 mFindIsUp = false;
2431 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002432 nativeSetFindIsDown();
2433 // Now that the dialog has been removed, ensure that we scroll to a
2434 // location that is not beyond the end of the page.
2435 pinScrollTo(mScrollX, mScrollY, false, 0);
2436 invalidate();
2437 }
2438
2439 /**
Cary Clark5bb6b522009-09-21 11:58:31 -04002440 * @hide
2441 */
2442 public void setFindDialogHeight(int height) {
2443 if (DebugFlags.WEB_VIEW) {
2444 Log.v(LOGTAG, "setFindDialogHeight height=" + height);
2445 }
2446 mFindHeight = height;
2447 }
2448
2449 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002450 * Query the document to see if it contains any image references. The
2451 * message object will be dispatched with arg1 being set to 1 if images
2452 * were found and 0 if the document does not reference any images.
2453 * @param response The message that will be dispatched with the result.
2454 */
2455 public void documentHasImages(Message response) {
2456 if (response == null) {
2457 return;
2458 }
2459 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
2460 }
2461
2462 @Override
2463 public void computeScroll() {
2464 if (mScroller.computeScrollOffset()) {
2465 int oldX = mScrollX;
2466 int oldY = mScrollY;
2467 mScrollX = mScroller.getCurrX();
2468 mScrollY = mScroller.getCurrY();
2469 postInvalidate(); // So we draw again
2470 if (oldX != mScrollX || oldY != mScrollY) {
2471 // as onScrollChanged() is not called, sendOurVisibleRect()
2472 // needs to be call explicitly
2473 sendOurVisibleRect();
2474 }
2475 } else {
2476 super.computeScroll();
2477 }
2478 }
2479
2480 private static int computeDuration(int dx, int dy) {
2481 int distance = Math.max(Math.abs(dx), Math.abs(dy));
2482 int duration = distance * 1000 / STD_SPEED;
2483 return Math.min(duration, MAX_DURATION);
2484 }
2485
2486 // helper to pin the scrollBy parameters (already in view coordinates)
2487 // returns true if the scroll was changed
2488 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
2489 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
2490 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002491 // helper to pin the scrollTo parameters (already in view coordinates)
2492 // returns true if the scroll was changed
2493 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
2494 x = pinLocX(x);
2495 y = pinLocY(y);
2496 int dx = x - mScrollX;
2497 int dy = y - mScrollY;
2498
2499 if ((dx | dy) == 0) {
2500 return false;
2501 }
Leon Scrogginsd55de402009-09-17 14:19:49 -04002502 if (animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002503 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002504 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
2505 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
Mike Cleronf116bf82009-09-27 19:14:12 -07002506 awakenScrollBars(mScroller.getDuration());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002507 invalidate();
2508 } else {
Cary Clark278ce052009-08-31 16:08:42 -04002509 abortAnimation(); // just in case
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002510 scrollTo(x, y);
2511 }
2512 return true;
2513 }
2514
2515 // Scale from content to view coordinates, and pin.
2516 // Also called by jni webview.cpp
Leon Scroggins4c943042009-07-31 15:05:42 -04002517 private boolean setContentScrollBy(int cx, int cy, boolean animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002518 if (mDrawHistory) {
2519 // disallow WebView to change the scroll position as History Picture
2520 // is used in the view system.
2521 // TODO: as we switchOutDrawHistory when trackball or navigation
2522 // keys are hit, this should be safe. Right?
Leon Scroggins4c943042009-07-31 15:05:42 -04002523 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002524 }
Mike Reede8853fc2009-09-04 14:01:48 -04002525 cx = contentToViewDimension(cx);
2526 cy = contentToViewDimension(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002527 if (mHeightCanMeasure) {
2528 // move our visible rect according to scroll request
2529 if (cy != 0) {
2530 Rect tempRect = new Rect();
2531 calcOurVisibleRect(tempRect);
2532 tempRect.offset(cx, cy);
2533 requestRectangleOnScreen(tempRect);
2534 }
2535 // FIXME: We scroll horizontally no matter what because currently
2536 // ScrollView and ListView will not scroll horizontally.
2537 // FIXME: Why do we only scroll horizontally if there is no
2538 // vertical scroll?
2539// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
Leon Scroggins4c943042009-07-31 15:05:42 -04002540 return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002541 } else {
Leon Scroggins4c943042009-07-31 15:05:42 -04002542 return pinScrollBy(cx, cy, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002543 }
2544 }
2545
2546 // scale from content to view coordinates, and pin
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002547 // return true if pin caused the final x/y different than the request cx/cy,
2548 // and a future scroll may reach the request cx/cy after our size has
2549 // changed
2550 // return false if the view scroll to the exact position as it is requested,
2551 // where negative numbers are taken to mean 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002552 private boolean setContentScrollTo(int cx, int cy) {
2553 if (mDrawHistory) {
2554 // disallow WebView to change the scroll position as History Picture
2555 // is used in the view system.
2556 // One known case where this is called is that WebCore tries to
2557 // restore the scroll position. As history Picture already uses the
2558 // saved scroll position, it is ok to skip this.
2559 return false;
2560 }
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002561 int vx;
2562 int vy;
2563 if ((cx | cy) == 0) {
2564 // If the page is being scrolled to (0,0), do not add in the title
2565 // bar's height, and simply scroll to (0,0). (The only other work
2566 // in contentToView_ is to multiply, so this would not change 0.)
2567 vx = 0;
2568 vy = 0;
2569 } else {
2570 vx = contentToViewX(cx);
2571 vy = contentToViewY(cy);
2572 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002573// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
2574// vx + " " + vy + "]");
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002575 // Some mobile sites attempt to scroll the title bar off the page by
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002576 // scrolling to (0,1). If we are at the top left corner of the
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002577 // page, assume this is an attempt to scroll off the title bar, and
2578 // animate the title bar off screen slowly enough that the user can see
2579 // it.
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002580 if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0) {
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002581 pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
2582 // Since we are animating, we have not yet reached the desired
2583 // scroll position. Do not return true to request another attempt
2584 return false;
2585 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002586 pinScrollTo(vx, vy, false, 0);
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002587 // If the request was to scroll to a negative coordinate, treat it as if
2588 // it was a request to scroll to 0
2589 if ((mScrollX != vx && cx >= 0) || (mScrollY != vy && cy >= 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002590 return true;
2591 } else {
2592 return false;
2593 }
2594 }
2595
2596 // scale from content to view coordinates, and pin
2597 private void spawnContentScrollTo(int cx, int cy) {
2598 if (mDrawHistory) {
2599 // disallow WebView to change the scroll position as History Picture
2600 // is used in the view system.
2601 return;
2602 }
Leon Scroggins0236e672009-09-02 21:12:08 -04002603 int vx = contentToViewX(cx);
2604 int vy = contentToViewY(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002605 pinScrollTo(vx, vy, true, 0);
2606 }
2607
2608 /**
2609 * These are from webkit, and are in content coordinate system (unzoomed)
2610 */
2611 private void contentSizeChanged(boolean updateLayout) {
2612 // suppress 0,0 since we usually see real dimensions soon after
2613 // this avoids drawing the prev content in a funny place. If we find a
2614 // way to consolidate these notifications, this check may become
2615 // obsolete
2616 if ((mContentWidth | mContentHeight) == 0) {
2617 return;
2618 }
2619
2620 if (mHeightCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002621 if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002622 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002623 requestLayout();
2624 }
2625 } else if (mWidthCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002626 if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002627 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002628 requestLayout();
2629 }
2630 } else {
2631 // If we don't request a layout, try to send our view size to the
2632 // native side to ensure that WebCore has the correct dimensions.
2633 sendViewSizeZoom();
2634 }
2635 }
2636
2637 /**
2638 * Set the WebViewClient that will receive various notifications and
2639 * requests. This will replace the current handler.
2640 * @param client An implementation of WebViewClient.
2641 */
2642 public void setWebViewClient(WebViewClient client) {
2643 mCallbackProxy.setWebViewClient(client);
2644 }
2645
2646 /**
Grace Kloba94ab3b62009-10-07 18:00:19 -07002647 * Gets the WebViewClient
2648 * @return the current WebViewClient instance.
2649 *
2650 *@hide pending API council approval.
2651 */
2652 public WebViewClient getWebViewClient() {
2653 return mCallbackProxy.getWebViewClient();
2654 }
2655
2656 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002657 * Register the interface to be used when content can not be handled by
2658 * the rendering engine, and should be downloaded instead. This will replace
2659 * the current handler.
2660 * @param listener An implementation of DownloadListener.
2661 */
2662 public void setDownloadListener(DownloadListener listener) {
2663 mCallbackProxy.setDownloadListener(listener);
2664 }
2665
2666 /**
2667 * Set the chrome handler. This is an implementation of WebChromeClient for
2668 * use in handling Javascript dialogs, favicons, titles, and the progress.
2669 * This will replace the current handler.
2670 * @param client An implementation of WebChromeClient.
2671 */
2672 public void setWebChromeClient(WebChromeClient client) {
2673 mCallbackProxy.setWebChromeClient(client);
2674 }
2675
2676 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01002677 * Gets the chrome handler.
2678 * @return the current WebChromeClient instance.
2679 *
2680 * @hide API council approval.
2681 */
2682 public WebChromeClient getWebChromeClient() {
2683 return mCallbackProxy.getWebChromeClient();
2684 }
2685
2686 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002687 * Set the Picture listener. This is an interface used to receive
2688 * notifications of a new Picture.
2689 * @param listener An implementation of WebView.PictureListener.
2690 */
2691 public void setPictureListener(PictureListener listener) {
2692 mPictureListener = listener;
2693 }
2694
2695 /**
2696 * {@hide}
2697 */
2698 /* FIXME: Debug only! Remove for SDK! */
2699 public void externalRepresentation(Message callback) {
2700 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
2701 }
2702
2703 /**
2704 * {@hide}
2705 */
2706 /* FIXME: Debug only! Remove for SDK! */
2707 public void documentAsText(Message callback) {
2708 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
2709 }
2710
2711 /**
2712 * Use this function to bind an object to Javascript so that the
2713 * methods can be accessed from Javascript.
2714 * <p><strong>IMPORTANT:</strong>
2715 * <ul>
2716 * <li> Using addJavascriptInterface() allows JavaScript to control your
2717 * application. This can be a very useful feature or a dangerous security
2718 * issue. When the HTML in the WebView is untrustworthy (for example, part
2719 * or all of the HTML is provided by some person or process), then an
2720 * attacker could inject HTML that will execute your code and possibly any
2721 * code of the attacker's choosing.<br>
2722 * Do not use addJavascriptInterface() unless all of the HTML in this
2723 * WebView was written by you.</li>
2724 * <li> The Java object that is bound runs in another thread and not in
2725 * the thread that it was constructed in.</li>
2726 * </ul></p>
2727 * @param obj The class instance to bind to Javascript
2728 * @param interfaceName The name to used to expose the class in Javascript
2729 */
2730 public void addJavascriptInterface(Object obj, String interfaceName) {
Cary Clarkded054c2009-06-15 10:26:08 -04002731 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
2732 arg.mObject = obj;
2733 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002734 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
2735 }
2736
2737 /**
2738 * Return the WebSettings object used to control the settings for this
2739 * WebView.
2740 * @return A WebSettings object that can be used to control this WebView's
2741 * settings.
2742 */
2743 public WebSettings getSettings() {
2744 return mWebViewCore.getSettings();
2745 }
2746
2747 /**
2748 * Return the list of currently loaded plugins.
2749 * @return The list of currently loaded plugins.
Andrei Popescu385df692009-08-13 11:59:57 +01002750 *
2751 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002752 */
Andrei Popescu385df692009-08-13 11:59:57 +01002753 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002754 public static synchronized PluginList getPluginList() {
Andrei Popescu385df692009-08-13 11:59:57 +01002755 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002756 }
2757
2758 /**
Andrei Popescu385df692009-08-13 11:59:57 +01002759 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002760 */
Andrei Popescu385df692009-08-13 11:59:57 +01002761 @Deprecated
2762 public void refreshPlugins(boolean reloadOpenPages) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002763
2764 //-------------------------------------------------------------------------
2765 // Override View methods
2766 //-------------------------------------------------------------------------
2767
2768 @Override
2769 protected void finalize() throws Throwable {
Cary Clark9a4c0632009-08-10 16:40:08 -04002770 try {
2771 destroy();
2772 } finally {
2773 super.finalize();
2774 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002775 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002776
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002777 @Override
Leon Scroggins0236e672009-09-02 21:12:08 -04002778 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
2779 if (child == mTitleBar) {
2780 // When drawing the title bar, move it horizontally to always show
2781 // at the top of the WebView.
2782 mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
2783 }
2784 return super.drawChild(canvas, child, drawingTime);
2785 }
2786
2787 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002788 protected void onDraw(Canvas canvas) {
2789 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
2790 if (mNativeClass == 0) {
2791 return;
2792 }
Leon Scroggins58992ea2009-09-17 15:55:31 -04002793 int saveCount = canvas.save();
2794 if (mTitleBar != null) {
2795 canvas.translate(0, (int) mTitleBar.getHeight());
2796 }
Grace Kloba04b28682009-09-14 14:38:37 -07002797 // Update the buttons in the picture, so when we draw the picture
2798 // to the screen, they are in the correct state.
2799 // Tell the native side if user is a) touching the screen,
2800 // b) pressing the trackball down, or c) pressing the enter key
2801 // If the cursor is on a button, we need to draw it in the pressed
2802 // state.
2803 // If mNativeClass is 0, we should not reach here, so we do not
2804 // need to check it again.
2805 nativeRecordButtons(hasFocus() && hasWindowFocus(),
2806 mTouchMode == TOUCH_SHORTPRESS_START_MODE
2807 || mTrackballDown || mGotCenterDown, false);
2808 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
Leon Scroggins0236e672009-09-02 21:12:08 -04002809 canvas.restoreToCount(saveCount);
Cary Clarkd6982c92009-05-29 11:02:22 -04002810
Leon Scroggins58992ea2009-09-17 15:55:31 -04002811 // Now draw the shadow.
2812 if (mTitleBar != null) {
2813 int y = mScrollY + getVisibleTitleHeight();
2814 int height = (int) (5f * getContext().getResources()
2815 .getDisplayMetrics().density);
2816 mTitleShadow.setBounds(mScrollX, y, mScrollX + getWidth(),
2817 y + height);
2818 mTitleShadow.draw(canvas);
2819 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002820 if (AUTO_REDRAW_HACK && mAutoRedraw) {
2821 invalidate();
2822 }
2823 }
2824
2825 @Override
2826 public void setLayoutParams(ViewGroup.LayoutParams params) {
2827 if (params.height == LayoutParams.WRAP_CONTENT) {
2828 mWrapContent = true;
2829 }
2830 super.setLayoutParams(params);
2831 }
2832
2833 @Override
2834 public boolean performLongClick() {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04002835 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
2836 // Send the click so that the textfield is in focus
Leon Scroggins1d96ca02009-10-23 11:49:03 -04002837 centerKeyPressOnTextField();
Leon Scroggins3ccd3652009-06-26 17:22:50 -04002838 rebuildWebTextView();
2839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002840 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002841 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002842 } else {
2843 return super.performLongClick();
2844 }
2845 }
2846
Grace Kloba94c715d2009-09-28 23:23:53 -07002847 boolean inAnimateZoom() {
2848 return mZoomScale != 0;
2849 }
2850
Leon Scroggins608f9f42009-09-03 10:06:04 -04002851 /**
2852 * Need to adjust the WebTextView after a change in zoom, since mActualScale
2853 * has changed. This is especially important for password fields, which are
2854 * drawn by the WebTextView, since it conveys more information than what
2855 * webkit draws. Thus we need to reposition it to show in the correct
2856 * place.
2857 */
2858 private boolean mNeedToAdjustWebTextView;
2859
Cary Clark5da9aeb2009-10-06 17:40:53 -04002860 private boolean didUpdateTextViewBounds(boolean allowIntersect) {
2861 Rect contentBounds = nativeFocusCandidateNodeBounds();
2862 Rect vBox = contentToViewRect(contentBounds);
2863 Rect visibleRect = new Rect();
2864 calcOurVisibleRect(visibleRect);
2865 if (allowIntersect ? Rect.intersects(visibleRect, vBox) :
2866 visibleRect.contains(vBox)) {
2867 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
2868 vBox.height());
2869 return true;
2870 } else {
2871 return false;
2872 }
2873 }
2874
Cary Clarkd6982c92009-05-29 11:02:22 -04002875 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002876 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002877 if (mDrawHistory) {
2878 canvas.scale(mActualScale, mActualScale);
2879 canvas.drawPicture(mHistoryPicture);
2880 return;
2881 }
2882
2883 boolean animateZoom = mZoomScale != 0;
Cary Clark25415e22009-10-12 13:41:28 -04002884 boolean animateScroll = (!mScroller.isFinished()
2885 || mVelocityTracker != null)
2886 && (mTouchMode != TOUCH_DRAG_MODE ||
2887 mHeldMotionless != MOTIONLESS_TRUE);
2888 if (mTouchMode == TOUCH_DRAG_MODE) {
2889 if (mHeldMotionless == MOTIONLESS_PENDING) {
2890 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
2891 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
2892 mHeldMotionless = MOTIONLESS_FALSE;
2893 }
2894 if (mHeldMotionless == MOTIONLESS_FALSE) {
2895 mPrivateHandler.sendMessageDelayed(mPrivateHandler
2896 .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
2897 mHeldMotionless = MOTIONLESS_PENDING;
2898 }
2899 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002900 if (animateZoom) {
2901 float zoomScale;
2902 int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
2903 if (interval < ZOOM_ANIMATION_LENGTH) {
2904 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
Cary Clarkd6982c92009-05-29 11:02:22 -04002905 zoomScale = 1.0f / (mInvInitialZoomScale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002906 + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
2907 invalidate();
2908 } else {
2909 zoomScale = mZoomScale;
2910 // set mZoomScale to be 0 as we have done animation
2911 mZoomScale = 0;
Grace Klobadfe095a2009-09-17 07:56:48 -07002912 // call invalidate() again to draw with the final filters
2913 invalidate();
Leon Scroggins608f9f42009-09-03 10:06:04 -04002914 if (mNeedToAdjustWebTextView) {
2915 mNeedToAdjustWebTextView = false;
Cary Clark5da9aeb2009-10-06 17:40:53 -04002916 // As a result of the zoom, the textfield is now on
2917 // screen. Place the WebTextView in its new place,
2918 // accounting for our new scroll/zoom values.
2919 if (didUpdateTextViewBounds(false)) {
Leon Scroggins10be7542009-09-30 18:01:38 -04002920 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
2921 contentToViewDimension(
2922 nativeFocusCandidateTextSize()));
Leon Scroggins10be7542009-09-30 18:01:38 -04002923 // If it is a password field, start drawing the
2924 // WebTextView once again.
2925 if (nativeFocusCandidateIsPassword()) {
2926 mWebTextView.setInPassword(true);
2927 }
2928 } else {
2929 // The textfield is now off screen. The user probably
2930 // was not zooming to see the textfield better. Remove
2931 // the WebTextView. If the user types a key, and the
2932 // textfield is still in focus, we will reconstruct
2933 // the WebTextView and scroll it back on screen.
2934 mWebTextView.remove();
Leon Scroggins608f9f42009-09-03 10:06:04 -04002935 }
2936 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002937 }
Grace Klobac0c03af2009-09-17 11:01:44 -07002938 // calculate the intermediate scroll position. As we need to use
2939 // zoomScale, we can't use pinLocX/Y directly. Copy the logic here.
Grace Kloba675c7d22009-07-23 09:21:21 -07002940 float scale = zoomScale * mInvInitialZoomScale;
2941 int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
2942 - mZoomCenterX);
2943 tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
2944 * zoomScale)) + mScrollX;
Grace Klobac0c03af2009-09-17 11:01:44 -07002945 int titleHeight = getTitleHeight();
2946 int ty = Math.round(scale
2947 * (mInitialScrollY + mZoomCenterY - titleHeight)
2948 - (mZoomCenterY - titleHeight));
2949 ty = -(ty <= titleHeight ? Math.max(ty, 0) : pinLoc(ty
2950 - titleHeight, getViewHeight(), Math.round(mContentHeight
2951 * zoomScale)) + titleHeight) + mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002952 canvas.translate(tx, ty);
2953 canvas.scale(zoomScale, zoomScale);
Leon Scroggins608f9f42009-09-03 10:06:04 -04002954 if (inEditingMode() && !mNeedToAdjustWebTextView
2955 && mZoomScale != 0) {
2956 // The WebTextView is up. Keep track of this so we can adjust
2957 // its size and placement when we finish zooming
2958 mNeedToAdjustWebTextView = true;
2959 // If it is in password mode, turn it off so it does not draw
2960 // misplaced.
2961 if (nativeFocusCandidateIsPassword()) {
2962 mWebTextView.setInPassword(false);
2963 }
2964 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002965 } else {
2966 canvas.scale(mActualScale, mActualScale);
2967 }
2968
2969 mWebViewCore.drawContentPicture(canvas, color, animateZoom,
2970 animateScroll);
2971
2972 if (mNativeClass == 0) return;
Cary Clarkbadd8392009-10-15 13:32:08 -04002973 if (mShiftIsPressed && !animateZoom) {
Cary Clark09e383c2009-10-26 16:43:58 -04002974 if (mTouchSelection || mExtendSelection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002975 nativeDrawSelectionRegion(canvas);
Cary Clark09e383c2009-10-26 16:43:58 -04002976 }
2977 if (!mTouchSelection) {
2978 nativeDrawSelectionPointer(canvas, mInvActualScale, mSelectX,
2979 mSelectY - getTitleHeight(), mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002980 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002981 } else if (drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002982 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
2983 mTouchMode = TOUCH_SHORTPRESS_MODE;
2984 HitTestResult hitTest = getHitTestResult();
2985 if (hitTest != null &&
2986 hitTest.mType != HitTestResult.UNKNOWN_TYPE) {
2987 mPrivateHandler.sendMessageDelayed(mPrivateHandler
2988 .obtainMessage(SWITCH_TO_LONGPRESS),
2989 LONG_PRESS_TIMEOUT);
2990 }
2991 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002992 nativeDrawCursorRing(canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002993 }
2994 // When the FindDialog is up, only draw the matches if we are not in
2995 // the process of scrolling them into view.
2996 if (mFindIsUp && !animateScroll) {
2997 nativeDrawMatches(canvas);
2998 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04002999 if (mFocusSizeChanged) {
3000 mFocusSizeChanged = false;
3001 didUpdateTextViewBounds(true);
3002 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003003 }
3004
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003005 // draw history
3006 private boolean mDrawHistory = false;
3007 private Picture mHistoryPicture = null;
3008 private int mHistoryWidth = 0;
3009 private int mHistoryHeight = 0;
3010
3011 // Only check the flag, can be called from WebCore thread
3012 boolean drawHistory() {
3013 return mDrawHistory;
3014 }
3015
3016 // Should only be called in UI thread
3017 void switchOutDrawHistory() {
3018 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Cary Clarkbc2e33b2009-04-23 13:12:19 -04003019 if (mDrawHistory && mWebViewCore.pictureReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003020 mDrawHistory = false;
3021 invalidate();
3022 int oldScrollX = mScrollX;
3023 int oldScrollY = mScrollY;
3024 mScrollX = pinLocX(mScrollX);
3025 mScrollY = pinLocY(mScrollY);
3026 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
3027 mUserScroll = false;
3028 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
3029 oldScrollY);
3030 }
3031 sendOurVisibleRect();
3032 }
3033 }
3034
Cary Clarkd6982c92009-05-29 11:02:22 -04003035 WebViewCore.CursorData cursorData() {
3036 WebViewCore.CursorData result = new WebViewCore.CursorData();
3037 result.mMoveGeneration = nativeMoveGeneration();
3038 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04003039 Point position = nativeCursorPosition();
3040 result.mX = position.x;
3041 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04003042 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003043 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003044
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003045 /**
3046 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04003047 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003048 * order, swap them.
3049 * @param start Beginning of selection to delete.
3050 * @param end End of selection to delete.
3051 */
3052 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003053 mTextGeneration++;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04003054 WebViewCore.TextSelectionData data
3055 = new WebViewCore.TextSelectionData(start, end);
3056 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
3057 data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003058 }
3059
3060 /**
3061 * Set the selection to (start, end) in the focused textfield. If start and
3062 * end are out of order, swap them.
3063 * @param start Beginning of selection.
3064 * @param end End of selection.
3065 */
3066 /* package */ void setSelection(int start, int end) {
Cary Clark2f1d60c2009-06-03 08:05:53 -04003067 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003068 }
3069
3070 // Called by JNI when a touch event puts a textfield into focus.
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003071 private void displaySoftKeyboard(boolean isTextView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003072 InputMethodManager imm = (InputMethodManager)
3073 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003074
3075 if (isTextView) {
Grace Klobab641abf2009-09-16 17:28:24 -07003076 if (mWebTextView == null) return;
3077
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003078 imm.showSoftInput(mWebTextView, 0);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003079 if (mInZoomOverview) {
3080 // if in zoom overview mode, call doDoubleTap() to bring it back
3081 // to normal mode so that user can enter text.
3082 doDoubleTap();
3083 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003084 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003085 else { // used by plugins
3086 imm.showSoftInput(this, 0);
3087 }
3088 }
3089
3090 // Called by WebKit to instruct the UI to hide the keyboard
3091 private void hideSoftKeyboard() {
3092 InputMethodManager imm = (InputMethodManager)
3093 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
3094
3095 imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003096 }
3097
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003098 /**
3099 * Only for calling from JNI. Allows a click on an unfocused textfield to
3100 * put the textfield in focus.
3101 */
3102 private void setOkayNotToMatch() {
3103 if (inEditingMode()) {
3104 mWebTextView.mOkayForFocusNotToMatch = true;
3105 }
3106 }
3107
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003108 /*
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003109 * This method checks the current focus and cursor and potentially rebuilds
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003110 * mWebTextView to have the appropriate properties, such as password,
3111 * multiline, and what text it contains. It also removes it if necessary.
3112 */
Leon Scroggins01058282009-07-30 16:33:56 -04003113 /* package */ void rebuildWebTextView() {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003114 // If the WebView does not have focus, do nothing until it gains focus.
Grace Kloba04b28682009-09-14 14:38:37 -07003115 if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003116 return;
3117 }
3118 boolean alreadyThere = inEditingMode();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003119 // inEditingMode can only return true if mWebTextView is non-null,
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003120 // so we can safely call remove() if (alreadyThere)
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003121 if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003122 if (alreadyThere) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003123 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003124 }
3125 return;
3126 }
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003127 // At this point, we know we have found an input field, so go ahead
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003128 // and create the WebTextView if necessary.
3129 if (mWebTextView == null) {
3130 mWebTextView = new WebTextView(mContext, WebView.this);
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003131 // Initialize our generation number.
3132 mTextGeneration = 0;
3133 }
Leon Scroggins3a6c88c2009-09-09 14:50:23 -04003134 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
3135 contentToViewDimension(nativeFocusCandidateTextSize()));
Cary Clark3524be92009-06-22 13:09:11 -04003136 Rect visibleRect = new Rect();
3137 calcOurContentVisibleRect(visibleRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003138 // Note that sendOurVisibleRect calls viewToContent, so the coordinates
3139 // should be in content coordinates.
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003140 Rect bounds = nativeFocusCandidateNodeBounds();
Cary Clarkd6982c92009-05-29 11:02:22 -04003141 if (!Rect.intersects(bounds, visibleRect)) {
Leon Scroggins40981262009-07-01 10:57:47 -04003142 mWebTextView.bringIntoView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003143 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003144 String text = nativeFocusCandidateText();
3145 int nodePointer = nativeFocusCandidatePointer();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003146 if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003147 // It is possible that we have the same textfield, but it has moved,
3148 // i.e. In the case of opening/closing the screen.
3149 // In that case, we need to set the dimensions, but not the other
3150 // aspects.
3151 // We also need to restore the selection, which gets wrecked by
3152 // calling setTextEntryRect.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003153 Spannable spannable = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003154 int start = Selection.getSelectionStart(spannable);
3155 int end = Selection.getSelectionEnd(spannable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003156 // If the text has been changed by webkit, update it. However, if
3157 // there has been more UI text input, ignore it. We will receive
3158 // another update when that text is recognized.
Cary Clarkd6982c92009-05-29 11:02:22 -04003159 if (text != null && !text.equals(spannable.toString())
3160 && nativeTextGeneration() == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003161 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003162 } else {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003163 // FIXME: Determine whether this is necessary.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003164 Selection.setSelection(spannable, start, end);
3165 }
3166 } else {
Mike Reede9e86b82009-09-15 11:26:53 -04003167 Rect vBox = contentToViewRect(bounds);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003168 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
3169 vBox.height());
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003170 mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
3171 Gravity.RIGHT : Gravity.NO_GRAVITY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003172 // this needs to be called before update adapter thread starts to
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003173 // ensure the mWebTextView has the same node pointer
3174 mWebTextView.setNodePointer(nodePointer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003175 int maxLength = -1;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003176 boolean isTextField = nativeFocusCandidateIsTextField();
Cary Clarkd6982c92009-05-29 11:02:22 -04003177 if (isTextField) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003178 maxLength = nativeFocusCandidateMaxLength();
3179 String name = nativeFocusCandidateName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003180 if (mWebViewCore.getSettings().getSaveFormData()
Cary Clarkd6982c92009-05-29 11:02:22 -04003181 && name != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003182 Message update = mPrivateHandler.obtainMessage(
Cary Clarkded054c2009-06-15 10:26:08 -04003183 REQUEST_FORM_DATA, nodePointer);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003184 RequestFormData updater = new RequestFormData(name,
3185 getUrl(), update);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003186 Thread t = new Thread(updater);
3187 t.start();
3188 }
3189 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003190 mWebTextView.setMaxLength(maxLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003191 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003192 mWebTextView.setAdapterCustom(adapter);
3193 mWebTextView.setSingleLine(isTextField);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003194 mWebTextView.setInPassword(nativeFocusCandidateIsPassword());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003195 if (null == text) {
Cary Clark243ea062009-06-25 10:49:32 -04003196 if (DebugFlags.WEB_VIEW) {
3197 Log.v(LOGTAG, "rebuildWebTextView null == text");
3198 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003199 text = "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003200 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003201 mWebTextView.setTextAndKeepSelection(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003202 mWebTextView.requestFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003203 }
3204 }
3205
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003206 /*
3207 * This class requests an Adapter for the WebTextView which shows past
3208 * entries stored in the database. It is a Runnable so that it can be done
3209 * in its own thread, without slowing down the UI.
3210 */
3211 private class RequestFormData implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003212 private String mName;
3213 private String mUrl;
3214 private Message mUpdateMessage;
3215
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003216 public RequestFormData(String name, String url, Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003217 mName = name;
3218 mUrl = url;
3219 mUpdateMessage = msg;
3220 }
3221
3222 public void run() {
3223 ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
3224 if (pastEntries.size() > 0) {
3225 AutoCompleteAdapter adapter = new
3226 AutoCompleteAdapter(mContext, pastEntries);
Cary Clarkded054c2009-06-15 10:26:08 -04003227 mUpdateMessage.obj = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003228 mUpdateMessage.sendToTarget();
3229 }
3230 }
3231 }
3232
Leon Scrogginse3225672009-06-03 15:53:13 -04003233 // This is used to determine long press with the center key. Does not
3234 // affect long press with the trackball/touch.
3235 private boolean mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003236
3237 @Override
3238 public boolean onKeyDown(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003239 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003240 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003241 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003242 }
3243
3244 if (mNativeClass == 0) {
3245 return false;
3246 }
3247
3248 // do this hack up front, so it always works, regardless of touch-mode
3249 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
3250 mAutoRedraw = !mAutoRedraw;
3251 if (mAutoRedraw) {
3252 invalidate();
3253 }
3254 return true;
3255 }
3256
3257 // Bubble up the key event if
3258 // 1. it is a system key; or
Grace Kloba04b28682009-09-14 14:38:37 -07003259 // 2. the host application wants to handle it;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003260 if (event.isSystem()
Grace Kloba04b28682009-09-14 14:38:37 -07003261 || mCallbackProxy.uiOverrideKeyEvent(event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003262 return false;
3263 }
3264
Cary Clark2f1d60c2009-06-03 08:05:53 -04003265 if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
Cary Clarkd6982c92009-05-29 11:02:22 -04003266 && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003267 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
Cary Clark09e383c2009-10-26 16:43:58 -04003268 setUpSelectXY();
3269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003270
3271 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3272 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3273 // always handle the navigation keys in the UI thread
3274 switchOutDrawHistory();
Cary Clarkc05af372009-10-16 10:52:27 -04003275 if (mShiftIsPressed) {
3276 int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
3277 ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
3278 int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
3279 -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0;
3280 int multiplier = event.getRepeatCount() + 1;
3281 moveSelection(xRate * multiplier, yRate * multiplier);
3282 return true;
3283 }
Cary Clark215b72c2009-06-26 14:38:43 -04003284 if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003285 playSoundEffect(keyCodeToSoundsEffect(keyCode));
3286 return true;
3287 }
3288 // Bubble up the key event as WebView doesn't handle it
3289 return false;
3290 }
3291
Leon Scrogginse3225672009-06-03 15:53:13 -04003292 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003293 switchOutDrawHistory();
3294 if (event.getRepeatCount() == 0) {
Cary Clarkc05af372009-10-16 10:52:27 -04003295 if (mShiftIsPressed) {
3296 return true; // discard press if copy in progress
3297 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003298 mGotCenterDown = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003299 mPrivateHandler.sendMessageDelayed(mPrivateHandler
Leon Scrogginse3225672009-06-03 15:53:13 -04003300 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003301 // Already checked mNativeClass, so we do not need to check it
3302 // again.
3303 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
3304 return true;
3305 }
3306 // Bubble up the key event as WebView doesn't handle it
3307 return false;
3308 }
3309
Cary Clark843bbb82009-04-20 16:03:31 -04003310 if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
3311 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
3312 // turn off copy select if a shift-key combo is pressed
3313 mExtendSelection = mShiftIsPressed = false;
3314 if (mTouchMode == TOUCH_SELECT_MODE) {
3315 mTouchMode = TOUCH_INIT_MODE;
3316 }
3317 }
3318
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003319 if (getSettings().getNavDump()) {
3320 switch (keyCode) {
3321 case KeyEvent.KEYCODE_4:
3322 // "/data/data/com.android.browser/displayTree.txt"
3323 nativeDumpDisplayTree(getUrl());
3324 break;
3325 case KeyEvent.KEYCODE_5:
3326 case KeyEvent.KEYCODE_6:
3327 // 5: dump the dom tree to the file
3328 // "/data/data/com.android.browser/domTree.txt"
3329 // 6: dump the dom tree to the adb log
3330 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE,
3331 (keyCode == KeyEvent.KEYCODE_5) ? 1 : 0, 0);
3332 break;
3333 case KeyEvent.KEYCODE_7:
3334 case KeyEvent.KEYCODE_8:
3335 // 7: dump the render tree to the file
3336 // "/data/data/com.android.browser/renderTree.txt"
3337 // 8: dump the render tree to the adb log
3338 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE,
3339 (keyCode == KeyEvent.KEYCODE_7) ? 1 : 0, 0);
3340 break;
3341 case KeyEvent.KEYCODE_9:
3342 nativeInstrumentReport();
3343 return true;
3344 }
3345 }
3346
Derek Sollenberger718d69f2009-10-19 15:56:43 -04003347 if (nativeCursorIsTextInput()) {
Leon Scroggins1cc24202009-06-16 10:10:28 -04003348 // This message will put the node in focus, for the DOM's notion
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003349 // of focus, and make the focuscontroller active
Leon Scroggins01058282009-07-30 16:33:56 -04003350 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
3351 nativeCursorNodePointer());
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003352 // This will bring up the WebTextView and put it in focus, for
3353 // our view system's notion of focus
3354 rebuildWebTextView();
3355 // Now we need to pass the event to it
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003356 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003357 mWebTextView.setDefaultSelection();
3358 mWebTextView.mOkayForFocusNotToMatch = true;
3359 return mWebTextView.dispatchKeyEvent(event);
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003360 }
Leon Scroggins40981262009-07-01 10:57:47 -04003361 } else if (nativeHasFocusNode()) {
3362 // In this case, the cursor is not on a text input, but the focus
3363 // might be. Check it, and if so, hand over to the WebTextView.
3364 rebuildWebTextView();
3365 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003366 return mWebTextView.dispatchKeyEvent(event);
Leon Scroggins40981262009-07-01 10:57:47 -04003367 }
Cary Clark19436562009-06-04 16:25:07 -04003368 }
3369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003370 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003371 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003372 // pass the key to DOM
3373 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
3374 // return true as DOM handles the key
3375 return true;
3376 }
3377
3378 // Bubble up the key event as WebView doesn't handle it
3379 return false;
3380 }
3381
3382 @Override
3383 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003384 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003385 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003386 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003387 }
3388
3389 if (mNativeClass == 0) {
3390 return false;
3391 }
3392
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003393 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04003394 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
3395 String text = nativeCursorText();
3396 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003397 && text.startsWith(SCHEME_TEL)) {
3398 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
3399 getContext().startActivity(intent);
3400 return true;
3401 }
3402 }
3403
3404 // Bubble up the key event if
3405 // 1. it is a system key; or
3406 // 2. the host application wants to handle it;
3407 if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
3408 return false;
3409 }
3410
Cary Clarkd6982c92009-05-29 11:02:22 -04003411 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003412 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
3413 if (commitCopy()) {
3414 return true;
3415 }
3416 }
3417
3418 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3419 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3420 // always handle the navigation keys in the UI thread
3421 // Bubble up the key event as WebView doesn't handle it
3422 return false;
3423 }
3424
Leon Scrogginse3225672009-06-03 15:53:13 -04003425 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003426 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04003427 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
3428 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003429
Leon Scrogginse3225672009-06-03 15:53:13 -04003430 if (mShiftIsPressed) {
Cary Clarkc05af372009-10-16 10:52:27 -04003431 if (mExtendSelection) {
3432 commitCopy();
3433 } else {
3434 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04003435 invalidate(); // draw the i-beam instead of the arrow
Cary Clarkc05af372009-10-16 10:52:27 -04003436 }
3437 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003438 }
Grace Klobadd817492009-09-14 10:17:06 -07003439
3440 // perform the single click
3441 Rect visibleRect = sendOurVisibleRect();
3442 // Note that sendOurVisibleRect calls viewToContent, so the
3443 // coordinates should be in content coordinates.
3444 if (!nativeCursorIntersects(visibleRect)) {
3445 return false;
3446 }
Grace Klobadd817492009-09-14 10:17:06 -07003447 WebViewCore.CursorData data = cursorData();
3448 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
3449 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003450 if (nativeCursorIsTextInput()) {
3451 rebuildWebTextView();
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003452 centerKeyPressOnTextField();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003453 if (inEditingMode()) {
3454 mWebTextView.setDefaultSelection();
3455 mWebTextView.mOkayForFocusNotToMatch = true;
3456 }
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003457 return true;
3458 }
3459 nativeSetFollowedLink(true);
3460 if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
Grace Klobadd817492009-09-14 10:17:06 -07003461 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
3462 nativeCursorNodePointer());
3463 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003464 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003465 }
3466
3467 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003468 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003469 // pass the key to DOM
3470 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
3471 // return true as DOM handles the key
3472 return true;
3473 }
3474
3475 // Bubble up the key event as WebView doesn't handle it
3476 return false;
3477 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003478
Cary Clark09e383c2009-10-26 16:43:58 -04003479 private void setUpSelectXY() {
3480 mExtendSelection = false;
3481 mShiftIsPressed = true;
3482 if (nativeHasCursorNode()) {
3483 Rect rect = nativeCursorNodeBounds();
3484 mSelectX = contentToViewX(rect.left);
3485 mSelectY = contentToViewY(rect.top);
3486 } else if (mLastTouchY > getVisibleTitleHeight()) {
3487 mSelectX = mScrollX + (int) mLastTouchX;
3488 mSelectY = mScrollY + (int) mLastTouchY;
3489 } else {
3490 mSelectX = mScrollX + getViewWidth() / 2;
3491 mSelectY = mScrollY + getViewHeightWithTitle() / 2;
3492 }
3493 nativeHideCursor();
3494 }
3495
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003496 /**
3497 * @hide
3498 */
3499 public void emulateShiftHeld() {
Cary Clark7f970112009-10-15 15:29:08 -04003500 if (0 == mNativeClass) return; // client isn't initialized
Cary Clark09e383c2009-10-26 16:43:58 -04003501 setUpSelectXY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003502 }
3503
3504 private boolean commitCopy() {
3505 boolean copiedSomething = false;
3506 if (mExtendSelection) {
3507 // copy region so core operates on copy without touching orig.
3508 Region selection = new Region(nativeGetSelection());
3509 if (selection.isEmpty() == false) {
3510 Toast.makeText(mContext
3511 , com.android.internal.R.string.text_copied
3512 , Toast.LENGTH_SHORT).show();
3513 mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection);
3514 copiedSomething = true;
3515 }
3516 mExtendSelection = false;
3517 }
3518 mShiftIsPressed = false;
Cary Clark09e383c2009-10-26 16:43:58 -04003519 invalidate(); // remove selection region and pointer
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003520 if (mTouchMode == TOUCH_SELECT_MODE) {
3521 mTouchMode = TOUCH_INIT_MODE;
3522 }
3523 return copiedSomething;
3524 }
3525
3526 // Set this as a hierarchy change listener so we can know when this view
3527 // is removed and still have access to our parent.
3528 @Override
3529 protected void onAttachedToWindow() {
3530 super.onAttachedToWindow();
3531 ViewParent parent = getParent();
3532 if (parent instanceof ViewGroup) {
3533 ViewGroup p = (ViewGroup) parent;
3534 p.setOnHierarchyChangeListener(this);
3535 }
3536 }
3537
3538 @Override
3539 protected void onDetachedFromWindow() {
3540 super.onDetachedFromWindow();
3541 ViewParent parent = getParent();
3542 if (parent instanceof ViewGroup) {
3543 ViewGroup p = (ViewGroup) parent;
3544 p.setOnHierarchyChangeListener(null);
3545 }
3546
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003547 // Clean up the zoom controller
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003548 mZoomButtonsController.setVisible(false);
3549 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003550
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003551 // Implementation for OnHierarchyChangeListener
3552 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04003553
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003554 public void onChildViewRemoved(View p, View child) {
3555 if (child == this) {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003556 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003557 }
3558 }
3559
3560 /**
3561 * @deprecated WebView should not have implemented
3562 * ViewTreeObserver.OnGlobalFocusChangeListener. This method
3563 * does nothing now.
3564 */
3565 @Deprecated
3566 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
3567 }
3568
Cary Clarkd6982c92009-05-29 11:02:22 -04003569 // To avoid drawing the cursor ring, and remove the TextView when our window
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003570 // loses focus.
3571 @Override
3572 public void onWindowFocusChanged(boolean hasWindowFocus) {
3573 if (hasWindowFocus) {
3574 if (hasFocus()) {
3575 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003576 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04003577 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003578 if (mNativeClass != 0) {
3579 nativeRecordButtons(true, false, true);
Leon Scroggins8cdad882009-06-30 08:47:04 -04003580 if (inEditingMode()) {
3581 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
3582 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003583 }
3584 } else {
3585 // If our window gained focus, but we do not have it, do not
Cary Clarkd6982c92009-05-29 11:02:22 -04003586 // draw the cursor ring.
3587 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003588 // We do not call nativeRecordButtons here because we assume
3589 // that when we lost focus, or window focus, it got called with
3590 // false for the first parameter
3591 }
3592 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07003593 if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003594 /*
3595 * The zoom controls come in their own window, so our window
Cary Clarkd6982c92009-05-29 11:02:22 -04003596 * loses focus. Our policy is to not draw the cursor ring if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003597 * our window is not focused, but this is an exception since
3598 * the user can still navigate the web page with the zoom
3599 * controls showing.
3600 */
Cary Clarkd6982c92009-05-29 11:02:22 -04003601 // If our window has lost focus, stop drawing the cursor ring
3602 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003603 }
3604 mGotKeyDown = false;
3605 mShiftIsPressed = false;
3606 if (mNativeClass != 0) {
3607 nativeRecordButtons(false, false, true);
3608 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003609 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003610 }
3611 invalidate();
3612 super.onWindowFocusChanged(hasWindowFocus);
3613 }
3614
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003615 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003616 * Pass a message to WebCore Thread, telling the WebCore::Page's
3617 * FocusController to be "inactive" so that it will
3618 * not draw the blinking cursor. It gets set to "active" to draw the cursor
3619 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003620 */
Leon Scroggins01058282009-07-30 16:33:56 -04003621 /* package */ void setFocusControllerInactive() {
Leon Scrogginsfd06bc82009-06-08 13:22:02 -04003622 // Do not need to also check whether mWebViewCore is null, because
3623 // mNativeClass is only set if mWebViewCore is non null
3624 if (mNativeClass == 0) return;
Leon Scroggins8cdad882009-06-30 08:47:04 -04003625 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003626 }
3627
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003628 @Override
3629 protected void onFocusChanged(boolean focused, int direction,
3630 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003631 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003632 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
3633 }
3634 if (focused) {
3635 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003636 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003637 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003638 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003639 if (mNativeClass != 0) {
3640 nativeRecordButtons(true, false, true);
3641 }
3642 //} else {
3643 // The WebView has gained focus while we do not have
3644 // windowfocus. When our window lost focus, we should have
3645 // called nativeRecordButtons(false...)
3646 }
3647 } else {
3648 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04003649 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003650 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003651 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003652 if (mNativeClass != 0) {
3653 nativeRecordButtons(false, false, true);
3654 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003655 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003656 }
3657 mGotKeyDown = false;
3658 }
3659
3660 super.onFocusChanged(focused, direction, previouslyFocusedRect);
3661 }
3662
Grace Kloba3f9faf42009-10-13 14:13:54 -07003663 /**
3664 * @hide
3665 */
3666 @Override
3667 protected boolean setFrame(int left, int top, int right, int bottom) {
3668 boolean changed = super.setFrame(left, top, right, bottom);
3669 if (!changed && mHeightCanMeasure) {
3670 // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
3671 // in WebViewCore after we get the first layout. We do call
3672 // requestLayout() when we get contentSizeChanged(). But the View
3673 // system won't call onSizeChanged if the dimension is not changed.
3674 // In this case, we need to call sendViewSizeZoom() explicitly to
3675 // notify the WebKit about the new dimensions.
3676 sendViewSizeZoom();
3677 }
3678 return changed;
3679 }
3680
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003681 @Override
3682 protected void onSizeChanged(int w, int h, int ow, int oh) {
3683 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003684 // Center zooming to the center of the screen.
Cary Clarka91874d2009-08-24 14:08:43 -04003685 if (mZoomScale == 0) { // unless we're already zooming
3686 mZoomCenterX = getViewWidth() * .5f;
3687 mZoomCenterY = getViewHeight() * .5f;
3688 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003689
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003690 // update mMinZoomScale if the minimum zoom scale is not fixed
3691 if (!mMinZoomScaleFixed) {
Grace Klobaba672802009-09-25 15:54:08 -07003692 // when change from narrow screen to wide screen, the new viewWidth
3693 // can be wider than the old content width. We limit the minimum
3694 // scale to 1.0f. The proper minimum scale will be calculated when
3695 // the new picture shows up.
3696 mMinZoomScale = Math.min(1.0f, (float) getViewWidth()
Grace Klobae397a882009-08-06 12:04:14 -07003697 / (mDrawHistory ? mHistoryPicture.getWidth()
Grace Klobaba672802009-09-25 15:54:08 -07003698 : mZoomOverviewWidth));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003699 }
3700
3701 // we always force, in case our height changed, in which case we still
3702 // want to send the notification over to webkit
3703 setNewZoomScale(mActualScale, true);
3704 }
3705
3706 @Override
3707 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
3708 super.onScrollChanged(l, t, oldl, oldt);
Patrick Scott0a5ce012009-07-02 08:56:10 -04003709
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003710 sendOurVisibleRect();
3711 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003712
3713
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003714 @Override
3715 public boolean dispatchKeyEvent(KeyEvent event) {
3716 boolean dispatch = true;
3717
3718 if (!inEditingMode()) {
3719 if (event.getAction() == KeyEvent.ACTION_DOWN) {
3720 mGotKeyDown = true;
3721 } else {
3722 if (!mGotKeyDown) {
3723 /*
3724 * We got a key up for which we were not the recipient of
3725 * the original key down. Don't give it to the view.
3726 */
3727 dispatch = false;
3728 }
3729 mGotKeyDown = false;
3730 }
3731 }
3732
3733 if (dispatch) {
3734 return super.dispatchKeyEvent(event);
3735 } else {
3736 // We didn't dispatch, so let something else handle the key
3737 return false;
3738 }
3739 }
3740
3741 // Here are the snap align logic:
3742 // 1. If it starts nearly horizontally or vertically, snap align;
3743 // 2. If there is a dramitic direction change, let it go;
3744 // 3. If there is a same direction back and forth, lock it.
3745
3746 // adjustable parameters
3747 private int mMinLockSnapReverseDistance;
3748 private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
3749 private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
3750
3751 @Override
3752 public boolean onTouchEvent(MotionEvent ev) {
3753 if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
3754 return false;
3755 }
3756
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003757 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003758 Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
3759 + mTouchMode);
3760 }
3761
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003762 int action = ev.getAction();
3763 float x = ev.getX();
3764 float y = ev.getY();
3765 long eventTime = ev.getEventTime();
3766
3767 // Due to the touch screen edge effect, a touch closer to the edge
3768 // always snapped to the edge. As getViewWidth() can be different from
3769 // getWidth() due to the scrollbar, adjusting the point to match
3770 // getViewWidth(). Same applied to the height.
3771 if (x > getViewWidth() - 1) {
3772 x = getViewWidth() - 1;
3773 }
Grace Kloba8eff73f2009-09-24 09:34:32 -07003774 if (y > getViewHeightWithTitle() - 1) {
3775 y = getViewHeightWithTitle() - 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003776 }
3777
3778 // pass the touch events from UI thread to WebCore thread
Grace Kloba04b28682009-09-14 14:38:37 -07003779 if (mForwardTouchEvents && (action != MotionEvent.ACTION_MOVE
3780 || eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003781 WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
3782 ted.mAction = action;
Leon Scroggins0236e672009-09-02 21:12:08 -04003783 ted.mX = viewToContentX((int) x + mScrollX);
3784 ted.mY = viewToContentY((int) y + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003785 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
3786 mLastSentTouchTime = eventTime;
3787 }
3788
Cary Clark25415e22009-10-12 13:41:28 -04003789 float fDeltaX = mLastTouchX - x;
3790 float fDeltaY = mLastTouchY - y;
3791 int deltaX = (int) fDeltaX;
3792 int deltaY = (int) fDeltaY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003793
3794 switch (action) {
3795 case MotionEvent.ACTION_DOWN: {
Grace Klobad66d84f2009-09-27 14:48:07 -07003796 mPreventDrag = PREVENT_DRAG_NO;
Grace Kloba04b28682009-09-14 14:38:37 -07003797 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04003798 // stop the current scroll animation, but if this is
3799 // the start of a fling, allow it to add to the current
3800 // fling's velocity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003801 mScroller.abortAnimation();
3802 mTouchMode = TOUCH_DRAG_START_MODE;
3803 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
3804 } else if (mShiftIsPressed) {
3805 mSelectX = mScrollX + (int) x;
3806 mSelectY = mScrollY + (int) y;
3807 mTouchMode = TOUCH_SELECT_MODE;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003808 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003809 Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
3810 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003811 nativeMoveSelection(viewToContentX(mSelectX),
3812 viewToContentY(mSelectY), false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003813 mTouchSelection = mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04003814 invalidate(); // draw the i-beam instead of the arrow
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003815 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
3816 mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
3817 if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
3818 mTouchMode = TOUCH_DOUBLE_TAP_MODE;
3819 } else {
3820 // commit the short press action for the previous tap
3821 doShortPress();
3822 // continue, mTouchMode should be still TOUCH_INIT_MODE
3823 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003824 } else {
3825 mTouchMode = TOUCH_INIT_MODE;
Grace Klobaf58af622009-09-24 17:41:23 -07003826 mPreventDrag = mForwardTouchEvents ? PREVENT_DRAG_MAYBE_YES
3827 : PREVENT_DRAG_NO;
Cary Clark77d98f42009-07-31 09:40:38 -04003828 mWebViewCore.sendMessage(
3829 EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003830 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
3831 EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
3832 (eventTime - mLastTouchUpTime), eventTime);
3833 }
3834 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003835 // Trigger the link
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003836 if (mTouchMode == TOUCH_INIT_MODE
3837 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003838 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3839 .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
3840 }
3841 // Remember where the motion event started
3842 mLastTouchX = x;
3843 mLastTouchY = y;
3844 mLastTouchTime = eventTime;
3845 mVelocityTracker = VelocityTracker.obtain();
3846 mSnapScrollMode = SNAP_NONE;
3847 break;
3848 }
3849 case MotionEvent.ACTION_MOVE: {
Grace Kloba04b28682009-09-14 14:38:37 -07003850 if (mTouchMode == TOUCH_DONE_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003851 // no dragging during scroll zoom animation
3852 break;
3853 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003854 mVelocityTracker.addMovement(ev);
3855
3856 if (mTouchMode != TOUCH_DRAG_MODE) {
3857 if (mTouchMode == TOUCH_SELECT_MODE) {
3858 mSelectX = mScrollX + (int) x;
3859 mSelectY = mScrollY + (int) y;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003860 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003861 Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
3862 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003863 nativeMoveSelection(viewToContentX(mSelectX),
3864 viewToContentY(mSelectY), true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003865 invalidate();
3866 break;
3867 }
Grace Klobaf58af622009-09-24 17:41:23 -07003868 if ((deltaX * deltaX + deltaY * deltaY) < mTouchSlopSquare) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003869 break;
3870 }
Grace Klobaf58af622009-09-24 17:41:23 -07003871 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
3872 // track mLastTouchTime as we may need to do fling at
3873 // ACTION_UP
3874 mLastTouchTime = eventTime;
3875 break;
3876 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003877 if (mTouchMode == TOUCH_SHORTPRESS_MODE
3878 || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3879 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003880 } else if (mTouchMode == TOUCH_INIT_MODE
3881 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003882 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
3883 }
3884
3885 // if it starts nearly horizontal or vertical, enforce it
3886 int ax = Math.abs(deltaX);
3887 int ay = Math.abs(deltaY);
3888 if (ax > MAX_SLOPE_FOR_DIAG * ay) {
3889 mSnapScrollMode = SNAP_X;
3890 mSnapPositive = deltaX > 0;
3891 } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
3892 mSnapScrollMode = SNAP_Y;
3893 mSnapPositive = deltaY > 0;
3894 }
3895
3896 mTouchMode = TOUCH_DRAG_MODE;
3897 WebViewCore.pauseUpdate(mWebViewCore);
Leon Scroggins72543e12009-07-23 15:29:45 -04003898 if (!mDragFromTextInput) {
3899 nativeHideCursor();
3900 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003901 WebSettings settings = getSettings();
Grace Kloba455e3af2009-08-13 11:01:21 -07003902 if (settings.supportZoom()
The Android Open Source Project10592532009-03-18 17:39:46 -07003903 && settings.getBuiltInZoomControls()
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003904 && !mZoomButtonsController.isVisible()
Grace Kloba04b28682009-09-14 14:38:37 -07003905 && mMinZoomScale < mMaxZoomScale) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003906 mZoomButtonsController.setVisible(true);
Grace Klobaf8d8b462009-09-20 15:57:49 -07003907 int count = settings.getDoubleTapToastCount();
3908 if (mInZoomOverview && count > 0) {
Grace Kloba24a3ff92009-09-22 10:42:22 -07003909 settings.setDoubleTapToastCount(--count);
Grace Klobaf8d8b462009-09-20 15:57:49 -07003910 Toast.makeText(mContext,
3911 com.android.internal.R.string.double_tap_toast,
Grace Kloba24a3ff92009-09-22 10:42:22 -07003912 Toast.LENGTH_LONG).show();
Grace Klobaf8d8b462009-09-20 15:57:49 -07003913 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003914 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003915 }
3916
3917 // do pan
3918 int newScrollX = pinLocX(mScrollX + deltaX);
Cary Clark25415e22009-10-12 13:41:28 -04003919 int newDeltaX = newScrollX - mScrollX;
3920 if (deltaX != newDeltaX) {
3921 deltaX = newDeltaX;
3922 fDeltaX = (float) newDeltaX;
3923 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003924 int newScrollY = pinLocY(mScrollY + deltaY);
Cary Clark25415e22009-10-12 13:41:28 -04003925 int newDeltaY = newScrollY - mScrollY;
3926 if (deltaY != newDeltaY) {
3927 deltaY = newDeltaY;
3928 fDeltaY = (float) newDeltaY;
3929 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003930 boolean done = false;
Cary Clark25415e22009-10-12 13:41:28 -04003931 boolean keepScrollBarsVisible = false;
3932 if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
3933 keepScrollBarsVisible = done = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003934 } else {
3935 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
3936 int ax = Math.abs(deltaX);
3937 int ay = Math.abs(deltaY);
3938 if (mSnapScrollMode == SNAP_X) {
3939 // radical change means getting out of snap mode
3940 if (ay > MAX_SLOPE_FOR_DIAG * ax
3941 && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
3942 mSnapScrollMode = SNAP_NONE;
3943 }
3944 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04003945 if (ax > MAX_SLOPE_FOR_DIAG * ay &&
3946 (mSnapPositive
3947 ? deltaX < -mMinLockSnapReverseDistance
3948 : deltaX > mMinLockSnapReverseDistance)) {
3949 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003950 }
3951 } else {
3952 // radical change means getting out of snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04003953 if (ax > MAX_SLOPE_FOR_DIAG * ay
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003954 && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
3955 mSnapScrollMode = SNAP_NONE;
3956 }
3957 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04003958 if (ay > MAX_SLOPE_FOR_DIAG * ax &&
3959 (mSnapPositive
3960 ? deltaY < -mMinLockSnapReverseDistance
3961 : deltaY > mMinLockSnapReverseDistance)) {
3962 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003963 }
3964 }
3965 }
Cary Clarkac492e12009-10-14 14:53:37 -04003966 if (mSnapScrollMode != SNAP_NONE) {
3967 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
3968 deltaY = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07003969 } else {
Cary Clarkac492e12009-10-14 14:53:37 -04003970 deltaX = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07003971 }
Cary Clarkac492e12009-10-14 14:53:37 -04003972 }
3973 if ((deltaX | deltaY) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003974 scrollBy(deltaX, deltaY);
Cary Clarkac492e12009-10-14 14:53:37 -04003975 if (deltaX != 0) {
3976 mLastTouchX = x;
3977 }
3978 if (deltaY != 0) {
3979 mLastTouchY = y;
3980 }
3981 mHeldMotionless = MOTIONLESS_FALSE;
3982 } else {
3983 // keep the scrollbar on the screen even there is no
3984 // scroll
3985 keepScrollBarsVisible = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003986 }
3987 mLastTouchTime = eventTime;
3988 mUserScroll = true;
3989 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003990
Grace Kloba455e3af2009-08-13 11:01:21 -07003991 if (!getSettings().getBuiltInZoomControls()) {
The Android Open Source Project10592532009-03-18 17:39:46 -07003992 boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
Grace Kloba04b28682009-09-14 14:38:37 -07003993 if (mZoomControls != null && showPlusMinus) {
The Android Open Source Project10592532009-03-18 17:39:46 -07003994 if (mZoomControls.getVisibility() == View.VISIBLE) {
3995 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
3996 } else {
Grace Kloba04b28682009-09-14 14:38:37 -07003997 mZoomControls.show(showPlusMinus, false);
The Android Open Source Project10592532009-03-18 17:39:46 -07003998 }
3999 mPrivateHandler.postDelayed(mZoomControlRunnable,
4000 ZOOM_CONTROLS_TIMEOUT);
4001 }
4002 }
4003
Cary Clark25415e22009-10-12 13:41:28 -04004004 if (keepScrollBarsVisible) {
4005 if (mHeldMotionless != MOTIONLESS_TRUE) {
4006 mHeldMotionless = MOTIONLESS_TRUE;
4007 invalidate();
4008 }
Grace Kloba5b2c0562009-09-30 10:17:13 -07004009 // keep the scrollbar on the screen even there is no scroll
4010 awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
4011 false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004012 // return false to indicate that we can't pan out of the
4013 // view space
Cary Clark25415e22009-10-12 13:41:28 -04004014 return !done;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004015 }
4016 break;
4017 }
4018 case MotionEvent.ACTION_UP: {
4019 mLastTouchUpTime = eventTime;
4020 switch (mTouchMode) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004021 case TOUCH_DOUBLE_TAP_MODE: // double tap
4022 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad3c6d542009-07-31 14:24:19 -07004023 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004024 doDoubleTap();
4025 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004026 case TOUCH_SELECT_MODE:
4027 commitCopy();
4028 mTouchSelection = false;
4029 break;
Grace Klobaf58af622009-09-24 17:41:23 -07004030 case TOUCH_INIT_MODE: // tap
Grace Klobad66d84f2009-09-27 14:48:07 -07004031 case TOUCH_SHORTPRESS_START_MODE:
4032 case TOUCH_SHORTPRESS_MODE:
Grace Klobaf58af622009-09-24 17:41:23 -07004033 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad66d84f2009-09-27 14:48:07 -07004034 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Klobaf58af622009-09-24 17:41:23 -07004035 if ((deltaX * deltaX + deltaY * deltaY) > mTouchSlopSquare) {
4036 Log.w(LOGTAG, "Miss a drag as we are waiting for" +
4037 " WebCore's response for touch down.");
4038 if (computeHorizontalScrollExtent() < computeHorizontalScrollRange()
4039 || computeVerticalScrollExtent() < computeVerticalScrollRange()) {
4040 // we will not rewrite drag code here, but we
4041 // will try fling if it applies.
4042 WebViewCore.pauseUpdate(mWebViewCore);
4043 // fall through to TOUCH_DRAG_MODE
4044 } else {
4045 break;
4046 }
4047 } else {
4048 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
4049 // if mPreventDrag is not confirmed, treat it as
4050 // no so that it won't block tap or double tap.
4051 mPreventDrag = PREVENT_DRAG_NO;
4052 }
4053 if (mPreventDrag == PREVENT_DRAG_NO) {
Grace Klobad66d84f2009-09-27 14:48:07 -07004054 if (mTouchMode == TOUCH_INIT_MODE) {
4055 mPrivateHandler.sendMessageDelayed(
4056 mPrivateHandler.obtainMessage(
4057 RELEASE_SINGLE_TAP),
4058 ViewConfiguration.getDoubleTapTimeout());
4059 } else {
4060 mTouchMode = TOUCH_DONE_MODE;
4061 doShortPress();
4062 }
Grace Klobaf58af622009-09-24 17:41:23 -07004063 }
4064 break;
4065 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004066 case TOUCH_DRAG_MODE:
Cary Clark25415e22009-10-12 13:41:28 -04004067 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4068 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
4069 mHeldMotionless = MOTIONLESS_TRUE;
Mike Reeddf4cf292009-09-15 14:31:54 -04004070 // redraw in high-quality, as we're done dragging
4071 invalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004072 // if the user waits a while w/o moving before the
4073 // up, we don't want to do a fling
4074 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
4075 mVelocityTracker.addMovement(ev);
4076 doFling();
4077 break;
4078 }
Cary Clark278ce052009-08-31 16:08:42 -04004079 mLastVelocity = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004080 WebViewCore.resumeUpdate(mWebViewCore);
4081 break;
4082 case TOUCH_DRAG_START_MODE:
4083 case TOUCH_DONE_MODE:
4084 // do nothing
4085 break;
4086 }
4087 // we also use mVelocityTracker == null to tell us that we are
4088 // not "moving around", so we can take the slower/prettier
4089 // mode in the drawing code
4090 if (mVelocityTracker != null) {
4091 mVelocityTracker.recycle();
4092 mVelocityTracker = null;
4093 }
4094 break;
4095 }
4096 case MotionEvent.ACTION_CANCEL: {
4097 // we also use mVelocityTracker == null to tell us that we are
4098 // not "moving around", so we can take the slower/prettier
4099 // mode in the drawing code
4100 if (mVelocityTracker != null) {
4101 mVelocityTracker.recycle();
4102 mVelocityTracker = null;
4103 }
Grace Kloba04b28682009-09-14 14:38:37 -07004104 if (mTouchMode == TOUCH_DRAG_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004105 WebViewCore.resumeUpdate(mWebViewCore);
4106 }
4107 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4108 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Cary Clark25415e22009-10-12 13:41:28 -04004109 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4110 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
4111 mHeldMotionless = MOTIONLESS_TRUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004112 mTouchMode = TOUCH_DONE_MODE;
Cary Clarke872f3a2009-06-11 09:51:11 -04004113 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004114 break;
4115 }
4116 }
4117 return true;
4118 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004119
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004120 private long mTrackballFirstTime = 0;
4121 private long mTrackballLastTime = 0;
4122 private float mTrackballRemainsX = 0.0f;
4123 private float mTrackballRemainsY = 0.0f;
4124 private int mTrackballXMove = 0;
4125 private int mTrackballYMove = 0;
4126 private boolean mExtendSelection = false;
4127 private boolean mTouchSelection = false;
4128 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
4129 private static final int TRACKBALL_TIMEOUT = 200;
4130 private static final int TRACKBALL_WAIT = 100;
4131 private static final int TRACKBALL_SCALE = 400;
4132 private static final int TRACKBALL_SCROLL_COUNT = 5;
4133 private static final int TRACKBALL_MOVE_COUNT = 10;
4134 private static final int TRACKBALL_MULTIPLIER = 3;
4135 private static final int SELECT_CURSOR_OFFSET = 16;
4136 private int mSelectX = 0;
4137 private int mSelectY = 0;
Cary Clark5da9aeb2009-10-06 17:40:53 -04004138 private boolean mFocusSizeChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004139 private boolean mShiftIsPressed = false;
4140 private boolean mTrackballDown = false;
4141 private long mTrackballUpTime = 0;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004142 private long mLastCursorTime = 0;
4143 private Rect mLastCursorBounds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004144
4145 // Set by default; BrowserActivity clears to interpret trackball data
Cary Clarkd6982c92009-05-29 11:02:22 -04004146 // directly for movement. Currently, the framework only passes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004147 // arrow key events, not trackball events, from one child to the next
4148 private boolean mMapTrackballToArrowKeys = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004150 public void setMapTrackballToArrowKeys(boolean setMap) {
4151 mMapTrackballToArrowKeys = setMap;
4152 }
4153
4154 void resetTrackballTime() {
4155 mTrackballLastTime = 0;
4156 }
4157
4158 @Override
4159 public boolean onTrackballEvent(MotionEvent ev) {
4160 long time = ev.getEventTime();
4161 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
4162 if (ev.getY() > 0) pageDown(true);
4163 if (ev.getY() < 0) pageUp(true);
4164 return true;
4165 }
4166 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Cary Clarkbadd8392009-10-15 13:32:08 -04004167 if (mShiftIsPressed) {
4168 return true; // discard press if copy in progress
4169 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004170 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004171 if (mNativeClass == 0) {
4172 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004173 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004174 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004175 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
4176 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
4177 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004178 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004179 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004180 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004181 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004182 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004183 }
4184 if (isInTouchMode()) requestFocusFromTouch();
4185 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04004186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004187 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04004188 // LONG_PRESS_CENTER is set in common onKeyDown
4189 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004190 mTrackballDown = false;
4191 mTrackballUpTime = time;
4192 if (mShiftIsPressed) {
4193 if (mExtendSelection) {
4194 commitCopy();
4195 } else {
4196 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04004197 invalidate(); // draw the i-beam instead of the arrow
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004198 }
Cary Clarkbadd8392009-10-15 13:32:08 -04004199 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004200 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004201 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004202 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004203 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004204 );
4205 }
4206 return false; // let common code in onKeyUp at it
4207 }
4208 if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004209 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004210 return false;
4211 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004212 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004213 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004214 return true; // discard move if trackball is down
4215 }
4216 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004217 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004218 return true;
4219 }
4220 // TODO: alternatively we can do panning as touch does
4221 switchOutDrawHistory();
4222 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004223 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004224 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004225 + time + " last=" + mTrackballLastTime);
4226 }
4227 mTrackballFirstTime = time;
4228 mTrackballXMove = mTrackballYMove = 0;
4229 }
4230 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004231 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004232 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
4233 }
4234 mTrackballRemainsX += ev.getX();
4235 mTrackballRemainsY += ev.getY();
4236 doTrackball(time);
4237 return true;
4238 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004239
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004240 void moveSelection(float xRate, float yRate) {
4241 if (mNativeClass == 0)
4242 return;
4243 int width = getViewWidth();
4244 int height = getViewHeight();
Cary Clarkc05af372009-10-16 10:52:27 -04004245 mSelectX += xRate;
4246 mSelectY += yRate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004247 int maxX = width + mScrollX;
4248 int maxY = height + mScrollY;
4249 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
4250 , mSelectX));
4251 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
4252 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004253 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004254 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004255 + " mSelectX=" + mSelectX
4256 + " mSelectY=" + mSelectY
4257 + " mScrollX=" + mScrollX
4258 + " mScrollY=" + mScrollY
4259 + " xRate=" + xRate
4260 + " yRate=" + yRate
4261 );
4262 }
Leon Scroggins0236e672009-09-02 21:12:08 -04004263 nativeMoveSelection(viewToContentX(mSelectX),
4264 viewToContentY(mSelectY), mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004265 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004266 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004267 : 0;
4268 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004269 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004270 : 0;
4271 pinScrollBy(scrollX, scrollY, true, 0);
4272 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
4273 requestRectangleOnScreen(select);
4274 invalidate();
4275 }
4276
4277 private int scaleTrackballX(float xRate, int width) {
4278 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
4279 int nextXMove = xMove;
4280 if (xMove > 0) {
4281 if (xMove > mTrackballXMove) {
4282 xMove -= mTrackballXMove;
4283 }
4284 } else if (xMove < mTrackballXMove) {
4285 xMove -= mTrackballXMove;
4286 }
4287 mTrackballXMove = nextXMove;
4288 return xMove;
4289 }
4290
4291 private int scaleTrackballY(float yRate, int height) {
4292 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
4293 int nextYMove = yMove;
4294 if (yMove > 0) {
4295 if (yMove > mTrackballYMove) {
4296 yMove -= mTrackballYMove;
4297 }
4298 } else if (yMove < mTrackballYMove) {
4299 yMove -= mTrackballYMove;
4300 }
4301 mTrackballYMove = nextYMove;
4302 return yMove;
4303 }
4304
4305 private int keyCodeToSoundsEffect(int keyCode) {
4306 switch(keyCode) {
4307 case KeyEvent.KEYCODE_DPAD_UP:
4308 return SoundEffectConstants.NAVIGATION_UP;
4309 case KeyEvent.KEYCODE_DPAD_RIGHT:
4310 return SoundEffectConstants.NAVIGATION_RIGHT;
4311 case KeyEvent.KEYCODE_DPAD_DOWN:
4312 return SoundEffectConstants.NAVIGATION_DOWN;
4313 case KeyEvent.KEYCODE_DPAD_LEFT:
4314 return SoundEffectConstants.NAVIGATION_LEFT;
4315 }
4316 throw new IllegalArgumentException("keyCode must be one of " +
4317 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
4318 "KEYCODE_DPAD_LEFT}.");
4319 }
4320
4321 private void doTrackball(long time) {
4322 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
4323 if (elapsed == 0) {
4324 elapsed = TRACKBALL_TIMEOUT;
4325 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004326 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004327 float yRate = mTrackballRemainsY * 1000 / elapsed;
Cary Clarkc05af372009-10-16 10:52:27 -04004328 int viewWidth = getViewWidth();
4329 int viewHeight = getViewHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004330 if (mShiftIsPressed) {
Cary Clarkc05af372009-10-16 10:52:27 -04004331 moveSelection(scaleTrackballX(xRate, viewWidth),
4332 scaleTrackballY(yRate, viewHeight));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004333 mTrackballRemainsX = mTrackballRemainsY = 0;
4334 return;
4335 }
4336 float ax = Math.abs(xRate);
4337 float ay = Math.abs(yRate);
4338 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004339 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004340 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
4341 + " xRate=" + xRate
4342 + " yRate=" + yRate
4343 + " mTrackballRemainsX=" + mTrackballRemainsX
4344 + " mTrackballRemainsY=" + mTrackballRemainsY);
4345 }
Cary Clarkc05af372009-10-16 10:52:27 -04004346 int width = mContentWidth - viewWidth;
4347 int height = mContentHeight - viewHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004348 if (width < 0) width = 0;
4349 if (height < 0) height = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004350 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
4351 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
4352 maxA = Math.max(ax, ay);
4353 int count = Math.max(0, (int) maxA);
4354 int oldScrollX = mScrollX;
4355 int oldScrollY = mScrollY;
4356 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004357 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
4358 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004359 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
4360 KeyEvent.KEYCODE_DPAD_RIGHT;
4361 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004362 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004363 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004364 + " count=" + count
4365 + " mTrackballRemainsX=" + mTrackballRemainsX
4366 + " mTrackballRemainsY=" + mTrackballRemainsY);
4367 }
Cary Clark215b72c2009-06-26 14:38:43 -04004368 if (navHandledKey(selectKeyCode, count, false, time, false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004369 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
4370 }
4371 mTrackballRemainsX = mTrackballRemainsY = 0;
4372 }
4373 if (count >= TRACKBALL_SCROLL_COUNT) {
4374 int xMove = scaleTrackballX(xRate, width);
4375 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004376 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004377 Log.v(LOGTAG, "doTrackball pinScrollBy"
4378 + " count=" + count
4379 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04004380 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
4381 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004382 );
4383 }
4384 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
4385 xMove = 0;
4386 }
4387 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
4388 yMove = 0;
4389 }
4390 if (xMove != 0 || yMove != 0) {
4391 pinScrollBy(xMove, yMove, true, 0);
4392 }
4393 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004394 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004395 }
4396
Mike Reede8853fc2009-09-04 14:01:48 -04004397 private int computeMaxScrollY() {
Grace Klobae621d6f2009-09-11 13:20:39 -07004398 int maxContentH = computeVerticalScrollRange() + getTitleHeight();
Cary Clark298d32e2009-09-14 14:53:24 -04004399 return Math.max(maxContentH - getHeight(), getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04004400 }
4401
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004402 public void flingScroll(int vx, int vy) {
4403 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04004404 int maxY = computeMaxScrollY();
Cary Clarkd6982c92009-05-29 11:02:22 -04004405
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004406 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
4407 invalidate();
4408 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004409
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004410 private void doFling() {
4411 if (mVelocityTracker == null) {
4412 return;
4413 }
4414 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04004415 int maxY = computeMaxScrollY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004416
Romain Guy4296fc42009-07-06 11:48:52 -07004417 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004418 int vx = (int) mVelocityTracker.getXVelocity();
4419 int vy = (int) mVelocityTracker.getYVelocity();
4420
4421 if (mSnapScrollMode != SNAP_NONE) {
Cary Clarkac492e12009-10-14 14:53:37 -04004422 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004423 vy = 0;
4424 } else {
4425 vx = 0;
4426 }
4427 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004428
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004429 if (true /* EMG release: make our fling more like Maps' */) {
4430 // maps cuts their velocity in half
4431 vx = vx * 3 / 4;
4432 vy = vy * 3 / 4;
4433 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04004434 if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
4435 WebViewCore.resumeUpdate(mWebViewCore);
4436 return;
4437 }
Cary Clark278ce052009-08-31 16:08:42 -04004438 float currentVelocity = mScroller.getCurrVelocity();
4439 if (mLastVelocity > 0 && currentVelocity > 0) {
4440 float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
4441 - Math.atan2(vy, vx)));
4442 final float circle = (float) (Math.PI) * 2.0f;
4443 if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
4444 vx += currentVelocity * mLastVelX / mLastVelocity;
4445 vy += currentVelocity * mLastVelY / mLastVelocity;
4446 if (DebugFlags.WEB_VIEW) {
4447 Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
4448 }
4449 } else if (DebugFlags.WEB_VIEW) {
4450 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
4451 }
4452 } else if (DebugFlags.WEB_VIEW) {
4453 Log.v(LOGTAG, "doFling start last=" + mLastVelocity
Cary Clarkaa7caa62009-09-08 14:15:07 -04004454 + " current=" + currentVelocity
4455 + " vx=" + vx + " vy=" + vy
4456 + " maxX=" + maxX + " maxY=" + maxY
4457 + " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
Cary Clark278ce052009-08-31 16:08:42 -04004458 }
4459 mLastVelX = vx;
4460 mLastVelY = vy;
4461 mLastVelocity = (float) Math.hypot(vx, vy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004462
4463 mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
4464 // TODO: duration is calculated based on velocity, if the range is
4465 // small, the animation will stop before duration is up. We may
4466 // want to calculate how long the animation is going to run to precisely
4467 // resume the webcore update.
4468 final int time = mScroller.getDuration();
4469 mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_UPDATE, time);
Mike Cleronf116bf82009-09-27 19:14:12 -07004470 awakenScrollBars(time);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004471 invalidate();
4472 }
4473
4474 private boolean zoomWithPreview(float scale) {
4475 float oldScale = mActualScale;
Grace Kloba675c7d22009-07-23 09:21:21 -07004476 mInitialScrollX = mScrollX;
4477 mInitialScrollY = mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004478
Grace Kloba25737912009-06-19 12:42:47 -07004479 // snap to DEFAULT_SCALE if it is close
Grace Kloba0d8b77c2009-06-25 11:20:51 -07004480 if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
4481 scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004482 }
4483
4484 setNewZoomScale(scale, false);
4485
4486 if (oldScale != mActualScale) {
4487 // use mZoomPickerScale to see zoom preview first
4488 mZoomStart = SystemClock.uptimeMillis();
4489 mInvInitialZoomScale = 1.0f / oldScale;
4490 mInvFinalZoomScale = 1.0f / mActualScale;
4491 mZoomScale = mActualScale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004492 if (!mInZoomOverview) {
4493 mLastScale = scale;
4494 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004495 invalidate();
4496 return true;
4497 } else {
4498 return false;
4499 }
4500 }
4501
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004502 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004503 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
4504 * in charge of installing this view to the view hierarchy. This view will
4505 * become visible when the user starts scrolling via touch and fade away if
4506 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004507 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07004508 * API version 3 introduces a built-in zoom mechanism that is shown
4509 * automatically by the MapView. This is the preferred approach for
4510 * showing the zoom UI.
4511 *
4512 * @deprecated The built-in zoom mechanism is preferred, see
4513 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004514 */
The Android Open Source Project10592532009-03-18 17:39:46 -07004515 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004516 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07004517 if (!getSettings().supportZoom()) {
4518 Log.w(LOGTAG, "This WebView doesn't support zoom.");
4519 return null;
4520 }
4521 if (mZoomControls == null) {
4522 mZoomControls = createZoomControls();
Cary Clarkd6982c92009-05-29 11:02:22 -04004523
The Android Open Source Project10592532009-03-18 17:39:46 -07004524 /*
4525 * need to be set to VISIBLE first so that getMeasuredHeight() in
4526 * {@link #onSizeChanged()} can return the measured value for proper
4527 * layout.
4528 */
4529 mZoomControls.setVisibility(View.VISIBLE);
4530 mZoomControlRunnable = new Runnable() {
4531 public void run() {
Cary Clarkd6982c92009-05-29 11:02:22 -04004532
The Android Open Source Project10592532009-03-18 17:39:46 -07004533 /* Don't dismiss the controls if the user has
4534 * focus on them. Wait and check again later.
4535 */
4536 if (!mZoomControls.hasFocus()) {
4537 mZoomControls.hide();
4538 } else {
4539 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4540 mPrivateHandler.postDelayed(mZoomControlRunnable,
4541 ZOOM_CONTROLS_TIMEOUT);
4542 }
4543 }
4544 };
4545 }
4546 return mZoomControls;
4547 }
4548
4549 private ExtendedZoomControls createZoomControls() {
4550 ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
4551 , null);
4552 zoomControls.setOnZoomInClickListener(new OnClickListener() {
4553 public void onClick(View v) {
4554 // reset time out
4555 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4556 mPrivateHandler.postDelayed(mZoomControlRunnable,
4557 ZOOM_CONTROLS_TIMEOUT);
4558 zoomIn();
4559 }
4560 });
4561 zoomControls.setOnZoomOutClickListener(new OnClickListener() {
4562 public void onClick(View v) {
4563 // reset time out
4564 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4565 mPrivateHandler.postDelayed(mZoomControlRunnable,
4566 ZOOM_CONTROLS_TIMEOUT);
4567 zoomOut();
4568 }
4569 });
The Android Open Source Project10592532009-03-18 17:39:46 -07004570 return zoomControls;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004571 }
4572
4573 /**
4574 * Gets the {@link ZoomButtonsController} which can be used to add
4575 * additional buttons to the zoom controls window.
Cary Clarkd6982c92009-05-29 11:02:22 -04004576 *
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004577 * @return The instance of {@link ZoomButtonsController} used by this class,
4578 * or null if it is unavailable.
The Android Open Source Project10592532009-03-18 17:39:46 -07004579 * @hide
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004580 */
4581 public ZoomButtonsController getZoomButtonsController() {
4582 return mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004583 }
4584
4585 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004586 * Perform zoom in in the webview
4587 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
4588 */
4589 public boolean zoomIn() {
4590 // TODO: alternatively we can disallow this during draw history mode
4591 switchOutDrawHistory();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004592 // Center zooming to the center of the screen.
Grace Kloba455e3af2009-08-13 11:01:21 -07004593 if (mInZoomOverview) {
4594 // if in overview mode, bring it back to normal mode
4595 mLastTouchX = getViewWidth() * .5f;
4596 mLastTouchY = getViewHeight() * .5f;
4597 doDoubleTap();
4598 return true;
4599 } else {
4600 mZoomCenterX = getViewWidth() * .5f;
4601 mZoomCenterY = getViewHeight() * .5f;
4602 return zoomWithPreview(mActualScale * 1.25f);
4603 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004604 }
4605
4606 /**
4607 * Perform zoom out in the webview
4608 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
4609 */
4610 public boolean zoomOut() {
4611 // TODO: alternatively we can disallow this during draw history mode
4612 switchOutDrawHistory();
Grace Klobae397a882009-08-06 12:04:14 -07004613 float scale = mActualScale * 0.8f;
Grace Kloba04b28682009-09-14 14:38:37 -07004614 if (scale < (mMinZoomScale + 0.1f)
Grace Klobae397a882009-08-06 12:04:14 -07004615 && mWebViewCore.getSettings().getUseWideViewPort()) {
4616 // when zoom out to min scale, switch to overview mode
4617 doDoubleTap();
4618 return true;
4619 } else {
4620 // Center zooming to the center of the screen.
4621 mZoomCenterX = getViewWidth() * .5f;
4622 mZoomCenterY = getViewHeight() * .5f;
4623 return zoomWithPreview(scale);
4624 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004625 }
4626
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004627 private void updateSelection() {
4628 if (mNativeClass == 0) {
4629 return;
4630 }
4631 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04004632 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
4633 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004634 Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
4635 contentX + mNavSlop, contentY + mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004636 nativeSelectBestAt(rect);
4637 }
4638
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004639 /**
Leon Scroggins72543e12009-07-23 15:29:45 -04004640 * Scroll the focused text field/area to match the WebTextView
Cary Clarkeaa18de2009-09-28 12:50:42 -04004641 * @param xPercent New x position of the WebTextView from 0 to 1.
Leon Scroggins72543e12009-07-23 15:29:45 -04004642 * @param y New y position of the WebTextView in view coordinates
4643 */
Cary Clarkeaa18de2009-09-28 12:50:42 -04004644 /*package*/ void scrollFocusedTextInput(float xPercent, int y) {
Leon Scroggins72543e12009-07-23 15:29:45 -04004645 if (!inEditingMode() || mWebViewCore == null) {
4646 return;
4647 }
Cary Clarkeaa18de2009-09-28 12:50:42 -04004648 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT,
Leon Scrogginsd3997e52009-09-21 14:15:18 -04004649 // Since this position is relative to the top of the text input
4650 // field, we do not need to take the title bar's height into
4651 // consideration.
Cary Clarkeaa18de2009-09-28 12:50:42 -04004652 viewToContentDimension(y),
4653 new Float(xPercent));
Leon Scroggins72543e12009-07-23 15:29:45 -04004654 }
4655
4656 /**
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004657 * Set our starting point and time for a drag from the WebTextView.
4658 */
4659 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
4660 if (!inEditingMode()) {
4661 return;
4662 }
4663 mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
4664 mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
4665 mLastTouchTime = eventTime;
4666 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04004667 abortAnimation();
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004668 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
4669 }
4670 mSnapScrollMode = SNAP_NONE;
4671 mVelocityTracker = VelocityTracker.obtain();
4672 mTouchMode = TOUCH_DRAG_START_MODE;
4673 }
4674
4675 /**
4676 * Given a motion event from the WebTextView, set its location to our
4677 * coordinates, and handle the event.
4678 */
4679 /*package*/ boolean textFieldDrag(MotionEvent event) {
4680 if (!inEditingMode()) {
4681 return false;
4682 }
Leon Scroggins72543e12009-07-23 15:29:45 -04004683 mDragFromTextInput = true;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004684 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
4685 (float) (mWebTextView.getTop() - mScrollY));
Leon Scroggins72543e12009-07-23 15:29:45 -04004686 boolean result = onTouchEvent(event);
4687 mDragFromTextInput = false;
4688 return result;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004689 }
4690
Leon Scroggins6679f2f2009-08-12 18:48:10 -04004691 /**
4692 * Do a touch up from a WebTextView. This will be handled by webkit to
4693 * change the selection.
4694 * @param event MotionEvent in the WebTextView's coordinates.
4695 */
4696 /*package*/ void touchUpOnTextField(MotionEvent event) {
4697 if (!inEditingMode()) {
4698 return;
4699 }
Leon Scroggins0236e672009-09-02 21:12:08 -04004700 int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
4701 int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
Cary Clarkdb6d9392009-09-15 13:13:23 -04004702 if (nativeFocusNodePointer() != nativeCursorNodePointer()) {
4703 nativeMotionUp(x, y, mNavSlop);
4704 }
Leon Scroggins6679f2f2009-08-12 18:48:10 -04004705 nativeTextInputMotionUp(x, y);
4706 }
4707
Leon Scroggins1d96ca02009-10-23 11:49:03 -04004708 /**
4709 * Called when pressing the center key or trackball on a textfield.
4710 */
4711 /*package*/ void centerKeyPressOnTextField() {
4712 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
4713 nativeCursorNodePointer());
4714 // Need to show the soft keyboard if it's not readonly.
4715 if (!nativeCursorIsReadOnly()) {
4716 displaySoftKeyboard(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004717 }
4718 }
4719
4720 private void doShortPress() {
4721 if (mNativeClass == 0) {
4722 return;
4723 }
4724 switchOutDrawHistory();
4725 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04004726 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
4727 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
Cary Clark8f9ff7e2009-05-14 10:09:26 -04004728 if (nativeMotionUp(contentX, contentY, mNavSlop)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004729 if (mLogEvent) {
4730 Checkin.updateStats(mContext.getContentResolver(),
4731 Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
4732 }
4733 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004734 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004735 playSoundEffect(SoundEffectConstants.CLICK);
4736 }
4737 }
4738
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004739 private void doDoubleTap() {
Leon Scroggins0236e672009-09-02 21:12:08 -04004740 if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004741 return;
4742 }
4743 mZoomCenterX = mLastTouchX;
4744 mZoomCenterY = mLastTouchY;
4745 mInZoomOverview = !mInZoomOverview;
Grace Klobad7660cc2009-08-17 17:13:01 -07004746 // remove the zoom control after double tap
Grace Klobaf8d8b462009-09-20 15:57:49 -07004747 WebSettings settings = getSettings();
4748 if (settings.getBuiltInZoomControls()) {
Grace Klobad7660cc2009-08-17 17:13:01 -07004749 if (mZoomButtonsController.isVisible()) {
4750 mZoomButtonsController.setVisible(false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004751 }
Grace Klobad7660cc2009-08-17 17:13:01 -07004752 } else {
4753 if (mZoomControlRunnable != null) {
4754 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4755 }
4756 if (mZoomControls != null) {
4757 mZoomControls.hide();
4758 }
4759 }
Grace Klobaf8d8b462009-09-20 15:57:49 -07004760 settings.setDoubleTapToastCount(0);
Grace Klobad7660cc2009-08-17 17:13:01 -07004761 if (mInZoomOverview) {
Grace Kloba17dd1ae2009-09-18 15:11:51 -07004762 // Force the titlebar fully reveal in overview mode
4763 if (mScrollY < getTitleHeight()) mScrollY = 0;
Grace Klobae397a882009-08-06 12:04:14 -07004764 zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004765 } else {
4766 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04004767 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
4768 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
Cary Clark77d98f42009-07-31 09:40:38 -04004769 int left = nativeGetBlockLeftEdge(contentX, contentY, mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004770 if (left != NO_LEFTEDGE) {
4771 // add a 5pt padding to the left edge. Re-calculate the zoom
4772 // center so that the new scroll x will be on the left edge.
4773 mZoomCenterX = left < 5 ? 0 : (left - 5) * mLastScale
4774 * mActualScale / (mLastScale - mActualScale);
4775 }
4776 zoomWithPreview(mLastScale);
4777 }
4778 }
4779
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004780 // Called by JNI to handle a touch on a node representing an email address,
4781 // address, or phone number
4782 private void overrideLoading(String url) {
4783 mCallbackProxy.uiOverrideUrlLoading(url);
4784 }
4785
4786 @Override
4787 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
4788 boolean result = false;
4789 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004790 result = mWebTextView.requestFocus(direction,
4791 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004792 } else {
4793 result = super.requestFocus(direction, previouslyFocusedRect);
4794 if (mWebViewCore.getSettings().getNeedInitialFocus()) {
4795 // For cases such as GMail, where we gain focus from a direction,
4796 // we want to move to the first available link.
4797 // FIXME: If there are no visible links, we may not want to
4798 int fakeKeyDirection = 0;
4799 switch(direction) {
4800 case View.FOCUS_UP:
4801 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
4802 break;
4803 case View.FOCUS_DOWN:
4804 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
4805 break;
4806 case View.FOCUS_LEFT:
4807 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
4808 break;
4809 case View.FOCUS_RIGHT:
4810 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
4811 break;
4812 default:
4813 return result;
4814 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004815 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Cary Clark215b72c2009-06-26 14:38:43 -04004816 navHandledKey(fakeKeyDirection, 1, true, 0, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004817 }
4818 }
4819 }
4820 return result;
4821 }
4822
4823 @Override
4824 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
4825 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4826
4827 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
4828 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
4829 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
4830 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
4831
4832 int measuredHeight = heightSize;
4833 int measuredWidth = widthSize;
4834
4835 // Grab the content size from WebViewCore.
Grace Klobae621d6f2009-09-11 13:20:39 -07004836 int contentHeight = contentToViewDimension(mContentHeight);
4837 int contentWidth = contentToViewDimension(mContentWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004838
4839// Log.d(LOGTAG, "------- measure " + heightMode);
4840
4841 if (heightMode != MeasureSpec.EXACTLY) {
4842 mHeightCanMeasure = true;
4843 measuredHeight = contentHeight;
4844 if (heightMode == MeasureSpec.AT_MOST) {
4845 // If we are larger than the AT_MOST height, then our height can
4846 // no longer be measured and we should scroll internally.
4847 if (measuredHeight > heightSize) {
4848 measuredHeight = heightSize;
4849 mHeightCanMeasure = false;
4850 }
4851 }
4852 } else {
4853 mHeightCanMeasure = false;
4854 }
4855 if (mNativeClass != 0) {
4856 nativeSetHeightCanMeasure(mHeightCanMeasure);
4857 }
4858 // For the width, always use the given size unless unspecified.
4859 if (widthMode == MeasureSpec.UNSPECIFIED) {
4860 mWidthCanMeasure = true;
4861 measuredWidth = contentWidth;
4862 } else {
4863 mWidthCanMeasure = false;
4864 }
4865
4866 synchronized (this) {
4867 setMeasuredDimension(measuredWidth, measuredHeight);
4868 }
4869 }
4870
4871 @Override
4872 public boolean requestChildRectangleOnScreen(View child,
4873 Rect rect,
4874 boolean immediate) {
4875 rect.offset(child.getLeft() - child.getScrollX(),
4876 child.getTop() - child.getScrollY());
4877
Grace Kloba8eff73f2009-09-24 09:34:32 -07004878 int height = getViewHeightWithTitle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004879 int screenTop = mScrollY;
4880 int screenBottom = screenTop + height;
4881
4882 int scrollYDelta = 0;
4883
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004884 if (rect.bottom > screenBottom) {
4885 int oneThirdOfScreenHeight = height / 3;
4886 if (rect.height() > 2 * oneThirdOfScreenHeight) {
4887 // If the rectangle is too tall to fit in the bottom two thirds
4888 // of the screen, place it at the top.
4889 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004890 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004891 // If the rectangle will still fit on screen, we want its
4892 // top to be in the top third of the screen.
4893 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004894 }
4895 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004896 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004897 }
4898
4899 int width = getWidth() - getVerticalScrollbarWidth();
4900 int screenLeft = mScrollX;
4901 int screenRight = screenLeft + width;
4902
4903 int scrollXDelta = 0;
4904
4905 if (rect.right > screenRight && rect.left > screenLeft) {
4906 if (rect.width() > width) {
4907 scrollXDelta += (rect.left - screenLeft);
4908 } else {
4909 scrollXDelta += (rect.right - screenRight);
4910 }
4911 } else if (rect.left < screenLeft) {
4912 scrollXDelta -= (screenLeft - rect.left);
4913 }
4914
4915 if ((scrollYDelta | scrollXDelta) != 0) {
4916 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
4917 }
4918
4919 return false;
4920 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004921
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004922 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
4923 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04004924 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
4925 arg.mReplace = replace;
4926 arg.mNewStart = newStart;
4927 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004928 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04004929 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004930 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
4931 }
4932
4933 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkded054c2009-06-15 10:26:08 -04004934 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
4935 arg.mEvent = event;
4936 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004937 // Increase our text generation number, and pass it to webcore thread
4938 mTextGeneration++;
4939 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
4940 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04004941 // To make sure the host application, like Browser, has the up to date
4942 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004943 // document state.
4944 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
4945 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04004946 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004947 }
4948
4949 /* package */ WebViewCore getWebViewCore() {
4950 return mWebViewCore;
4951 }
4952
4953 //-------------------------------------------------------------------------
4954 // Methods can be called from a separate thread, like WebViewCore
4955 // If it needs to call the View system, it has to send message.
4956 //-------------------------------------------------------------------------
4957
4958 /**
4959 * General handler to receive message coming from webkit thread
4960 */
4961 class PrivateHandler extends Handler {
4962 @Override
4963 public void handleMessage(Message msg) {
Cary Clark3e88ddc2009-10-08 14:59:46 -04004964 // exclude INVAL_RECT_MSG_ID since it is frequently output
4965 if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004966 Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
Cary Clark3e88ddc2009-10-08 14:59:46 -04004967 > REQUEST_KEYBOARD ? Integer.toString(msg.what)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004968 : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
4969 }
Grace Kloba207308a2009-09-27 11:44:14 -07004970 if (mWebViewCore == null) {
4971 // after WebView's destroy() is called, skip handling messages.
4972 return;
4973 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004974 switch (msg.what) {
4975 case REMEMBER_PASSWORD: {
4976 mDatabase.setUsernamePassword(
4977 msg.getData().getString("host"),
4978 msg.getData().getString("username"),
4979 msg.getData().getString("password"));
4980 ((Message) msg.obj).sendToTarget();
4981 break;
4982 }
4983 case NEVER_REMEMBER_PASSWORD: {
4984 mDatabase.setUsernamePassword(
4985 msg.getData().getString("host"), null, null);
4986 ((Message) msg.obj).sendToTarget();
4987 break;
4988 }
4989 case SWITCH_TO_SHORTPRESS: {
Grace Klobaf58af622009-09-24 17:41:23 -07004990 // if mPreventDrag is not confirmed, treat it as no so that
4991 // it won't block panning the page.
4992 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
4993 mPreventDrag = PREVENT_DRAG_NO;
4994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004995 if (mTouchMode == TOUCH_INIT_MODE) {
4996 mTouchMode = TOUCH_SHORTPRESS_START_MODE;
4997 updateSelection();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004998 } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
4999 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005000 }
5001 break;
5002 }
5003 case SWITCH_TO_LONGPRESS: {
Grace Klobaf58af622009-09-24 17:41:23 -07005004 if (mPreventDrag == PREVENT_DRAG_NO) {
Grace Kloba6c451b72009-06-25 12:25:30 -07005005 mTouchMode = TOUCH_DONE_MODE;
5006 performLongClick();
Grace Kloba959046c2009-06-25 14:28:04 -07005007 rebuildWebTextView();
Grace Kloba6c451b72009-06-25 12:25:30 -07005008 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005009 break;
5010 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005011 case RELEASE_SINGLE_TAP: {
Grace Klobaf58af622009-09-24 17:41:23 -07005012 if (mPreventDrag == PREVENT_DRAG_NO) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005013 mTouchMode = TOUCH_DONE_MODE;
5014 doShortPress();
5015 }
5016 break;
5017 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005018 case SCROLL_BY_MSG_ID:
5019 setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
5020 break;
5021 case SYNC_SCROLL_TO_MSG_ID:
5022 if (mUserScroll) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005023 // if user has scrolled explicitly, don't sync the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005024 // scroll position any more
5025 mUserScroll = false;
5026 break;
5027 }
5028 // fall through
5029 case SCROLL_TO_MSG_ID:
5030 if (setContentScrollTo(msg.arg1, msg.arg2)) {
5031 // if we can't scroll to the exact position due to pin,
Cary Clarkd6982c92009-05-29 11:02:22 -04005032 // send a message to WebCore to re-scroll when we get a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005033 // new picture
5034 mUserScroll = false;
5035 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
5036 msg.arg1, msg.arg2);
5037 }
5038 break;
5039 case SPAWN_SCROLL_TO_MSG_ID:
5040 spawnContentScrollTo(msg.arg1, msg.arg2);
5041 break;
Grace Klobae397a882009-08-06 12:04:14 -07005042 case NEW_PICTURE_MSG_ID: {
5043 WebSettings settings = mWebViewCore.getSettings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005044 // called for new content
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005045 final int viewWidth = getViewWidth();
Cary Clarkd6982c92009-05-29 11:02:22 -04005046 final WebViewCore.DrawData draw =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005047 (WebViewCore.DrawData) msg.obj;
5048 final Point viewSize = draw.mViewPoint;
Grace Klobae397a882009-08-06 12:04:14 -07005049 boolean useWideViewport = settings.getUseWideViewPort();
Grace Klobaef347ef2009-07-30 11:20:32 -07005050 WebViewCore.RestoreState restoreState = draw.mRestoreState;
5051 if (restoreState != null) {
5052 mInZoomOverview = false;
5053 mLastScale = restoreState.mTextWrapScale;
5054 if (restoreState.mMinScale == 0) {
Grace Kloba3c72fff2009-09-04 13:15:59 -07005055 if (restoreState.mMobileSite) {
Cary Clarkb82665e2009-09-23 12:49:28 -04005056 if (draw.mMinPrefWidth >
5057 Math.max(0, draw.mViewPoint.x)) {
Grace Kloba3c72fff2009-09-04 13:15:59 -07005058 mMinZoomScale = (float) viewWidth
5059 / draw.mMinPrefWidth;
5060 mMinZoomScaleFixed = false;
5061 } else {
Grace Kloba408cf852009-09-20 16:34:44 -07005062 mMinZoomScale = restoreState.mDefaultScale;
Grace Kloba3c72fff2009-09-04 13:15:59 -07005063 mMinZoomScaleFixed = true;
5064 }
5065 } else {
5066 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
5067 mMinZoomScaleFixed = false;
5068 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005069 } else {
5070 mMinZoomScale = restoreState.mMinScale;
5071 mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005072 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005073 if (restoreState.mMaxScale == 0) {
5074 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
5075 } else {
5076 mMaxZoomScale = restoreState.mMaxScale;
5077 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005078 setNewZoomScale(mLastScale, false);
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04005079 setContentScrollTo(restoreState.mScrollX,
5080 restoreState.mScrollY);
Grace Kloba04b28682009-09-14 14:38:37 -07005081 if (useWideViewport
Grace Kloba3c72fff2009-09-04 13:15:59 -07005082 && settings.getLoadWithOverviewMode()) {
5083 if (restoreState.mViewScale == 0
5084 || (restoreState.mMobileSite
Grace Kloba408cf852009-09-20 16:34:44 -07005085 && mMinZoomScale < restoreState.mDefaultScale)) {
Leon Scrogginsb2359262009-08-19 16:19:26 -04005086 mInZoomOverview = true;
Leon Scrogginsb2359262009-08-19 16:19:26 -04005087 }
5088 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005089 // As we are on a new page, remove the WebTextView. This
5090 // is necessary for page loads driven by webkit, and in
5091 // particular when the user was on a password field, so
5092 // the WebTextView was visible.
5093 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005094 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005095 // We update the layout (i.e. request a layout from the
5096 // view system) if the last view size that we sent to
5097 // WebCore matches the view size of the picture we just
5098 // received in the fixed dimension.
5099 final boolean updateLayout = viewSize.x == mLastWidthSent
5100 && viewSize.y == mLastHeightSent;
Cary Clarkd6982c92009-05-29 11:02:22 -04005101 recordNewContentSize(draw.mWidthHeight.x,
Cary Clark5bb6b522009-09-21 11:58:31 -04005102 draw.mWidthHeight.y
5103 + (mFindIsUp ? mFindHeight : 0), updateLayout);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005104 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005105 Rect b = draw.mInvalRegion.getBounds();
5106 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
5107 b.left+","+b.top+","+b.right+","+b.bottom+"}");
5108 }
Mike Reede9e86b82009-09-15 11:26:53 -04005109 invalidateContentRect(draw.mInvalRegion.getBounds());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005110 if (mPictureListener != null) {
5111 mPictureListener.onNewPicture(WebView.this, capturePicture());
5112 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005113 if (useWideViewport) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005114 mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
5115 draw.mViewPoint.x);
5116 }
5117 if (!mMinZoomScaleFixed) {
Grace Klobae397a882009-08-06 12:04:14 -07005118 mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005119 }
5120 if (!mDrawHistory && mInZoomOverview) {
5121 // fit the content width to the current view. Ignore
5122 // the rounding error case.
5123 if (Math.abs((viewWidth * mInvActualScale)
5124 - mZoomOverviewWidth) > 1) {
Grace Klobae397a882009-08-06 12:04:14 -07005125 setNewZoomScale((float) viewWidth
5126 / mZoomOverviewWidth, false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005127 }
5128 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04005129 if (draw.mFocusSizeChanged && inEditingMode()) {
5130 mFocusSizeChanged = true;
5131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005132 break;
Grace Klobae397a882009-08-06 12:04:14 -07005133 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005134 case WEBCORE_INITIALIZED_MSG_ID:
5135 // nativeCreate sets mNativeClass to a non-zero value
5136 nativeCreate(msg.arg1);
5137 break;
5138 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
5139 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04005140 // and representing the same node as the pointer.
5141 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005142 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005143 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005144 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005145 int start = Selection.getSelectionStart(text);
5146 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005147 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005148 // Restore the selection, which may have been
5149 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005150 Spannable pword =
5151 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005152 Selection.setSelection(pword, start, end);
5153 // If the text entry has created more events, ignore
5154 // this one.
5155 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005156 mWebTextView.setTextAndKeepSelection(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005157 (String) msg.obj);
5158 }
5159 }
5160 break;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005161 case UPDATE_TEXT_SELECTION_MSG_ID:
5162 if (inEditingMode()
5163 && mWebTextView.isSameTextField(msg.arg1)
5164 && msg.arg2 == mTextGeneration) {
5165 WebViewCore.TextSelectionData tData
5166 = (WebViewCore.TextSelectionData) msg.obj;
5167 mWebTextView.setSelectionFromWebKit(tData.mStart,
5168 tData.mEnd);
5169 }
5170 break;
Cary Clark215b72c2009-06-26 14:38:43 -04005171 case MOVE_OUT_OF_PLUGIN:
Derek Sollenberger718d69f2009-10-19 15:56:43 -04005172 navHandledKey(msg.arg1, 1, false, 0, true);
Cary Clark215b72c2009-06-26 14:38:43 -04005173 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005174 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04005175 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005176 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04005177 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005178 mWebTextView.bringIntoView();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005179 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005180 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005181 break;
Cary Clark243ea062009-06-25 10:49:32 -04005182 case CLEAR_TEXT_ENTRY:
5183 clearTextEntry();
5184 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005185 case INVAL_RECT_MSG_ID: {
5186 Rect r = (Rect)msg.obj;
5187 if (r == null) {
5188 invalidate();
5189 } else {
5190 // we need to scale r from content into view coords,
5191 // which viewInvalidate() does for us
5192 viewInvalidate(r.left, r.top, r.right, r.bottom);
5193 }
5194 break;
5195 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005196 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04005197 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005198 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005199 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005200 }
5201 break;
5202 case UPDATE_CLIPBOARD:
5203 String str = (String) msg.obj;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005204 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005205 Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
5206 }
5207 try {
5208 IClipboard clip = IClipboard.Stub.asInterface(
5209 ServiceManager.getService("clipboard"));
5210 clip.setClipboardText(str);
5211 } catch (android.os.RemoteException e) {
5212 Log.e(LOGTAG, "Clipboard failed", e);
5213 }
5214 break;
5215 case RESUME_WEBCORE_UPDATE:
5216 WebViewCore.resumeUpdate(mWebViewCore);
5217 break;
5218
Leon Scrogginse3225672009-06-03 15:53:13 -04005219 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005220 // as this is shared by keydown and trackballdown, reset all
5221 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04005222 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005223 mTrackballDown = false;
Leon Scrogginse3225672009-06-03 15:53:13 -04005224 // LONG_PRESS_CENTER is sent as a delayed message. If we
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005225 // switch to windows overview, the WebView will be
5226 // temporarily removed from the view system. In that case,
5227 // do nothing.
5228 if (getParent() != null) {
5229 performLongClick();
5230 }
5231 break;
5232
5233 case WEBCORE_NEED_TOUCH_EVENTS:
5234 mForwardTouchEvents = (msg.arg1 != 0);
5235 break;
5236
5237 case PREVENT_TOUCH_ID:
5238 if (msg.arg1 == MotionEvent.ACTION_DOWN) {
Grace Klobaf58af622009-09-24 17:41:23 -07005239 // dont override if mPreventDrag has been set to no due
5240 // to time out
5241 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
5242 mPreventDrag = msg.arg2 == 1 ? PREVENT_DRAG_YES
5243 : PREVENT_DRAG_NO;
5244 if (mPreventDrag == PREVENT_DRAG_YES) {
5245 mTouchMode = TOUCH_DONE_MODE;
5246 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005247 }
5248 }
5249 break;
5250
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005251 case REQUEST_KEYBOARD:
5252 if (msg.arg1 == 0) {
5253 hideSoftKeyboard();
5254 } else {
5255 displaySoftKeyboard(false);
Cary Clark3e88ddc2009-10-08 14:59:46 -04005256 if (DebugFlags.WEB_VIEW) {
5257 Log.v(LOGTAG, "REQUEST_KEYBOARD"
5258 + " focusCandidateIsPlugin="
5259 + nativeFocusCandidateIsPlugin());
5260 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005261 }
5262 break;
5263
Cary Clark25415e22009-10-12 13:41:28 -04005264 case DRAG_HELD_MOTIONLESS:
5265 mHeldMotionless = MOTIONLESS_TRUE;
5266 invalidate();
5267 // fall through to keep scrollbars awake
5268
5269 case AWAKEN_SCROLL_BARS:
5270 if (mTouchMode == TOUCH_DRAG_MODE
5271 && mHeldMotionless == MOTIONLESS_TRUE) {
5272 awakenScrollBars(ViewConfiguration
5273 .getScrollDefaultDelay(), false);
5274 mPrivateHandler.sendMessageDelayed(mPrivateHandler
5275 .obtainMessage(AWAKEN_SCROLL_BARS),
5276 ViewConfiguration.getScrollDefaultDelay());
5277 }
5278 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005279 default:
5280 super.handleMessage(msg);
5281 break;
5282 }
5283 }
5284 }
5285
5286 // Class used to use a dropdown for a <select> element
5287 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005288 // Whether the listbox allows multiple selection.
5289 private boolean mMultiple;
5290 // Passed in to a list with multiple selection to tell
5291 // which items are selected.
5292 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04005293 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005294 // where the initial selection is.
5295 private int mSelection;
5296
5297 private Container[] mContainers;
5298
5299 // Need these to provide stable ids to my ArrayAdapter,
5300 // which normally does not have stable ids. (Bug 1250098)
5301 private class Container extends Object {
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005302 /**
5303 * Possible values for mEnabled. Keep in sync with OptionStatus in
5304 * WebViewCore.cpp
5305 */
5306 final static int OPTGROUP = -1;
5307 final static int OPTION_DISABLED = 0;
5308 final static int OPTION_ENABLED = 1;
5309
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005310 String mString;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005311 int mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005312 int mId;
5313
5314 public String toString() {
5315 return mString;
5316 }
5317 }
5318
5319 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04005320 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005321 * and allow filtering.
5322 */
5323 private class MyArrayListAdapter extends ArrayAdapter<Container> {
5324 public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005325 super(context,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005326 multiple ? com.android.internal.R.layout.select_dialog_multichoice :
Cary Clarkd6982c92009-05-29 11:02:22 -04005327 com.android.internal.R.layout.select_dialog_singlechoice,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005328 objects);
5329 }
5330
5331 @Override
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005332 public View getView(int position, View convertView,
5333 ViewGroup parent) {
5334 // Always pass in null so that we will get a new CheckedTextView
5335 // Otherwise, an item which was previously used as an <optgroup>
5336 // element (i.e. has no check), could get used as an <option>
5337 // element, which needs a checkbox/radio, but it would not have
5338 // one.
5339 convertView = super.getView(position, null, parent);
5340 Container c = item(position);
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -04005341 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
5342 // ListView does not draw dividers between disabled and
5343 // enabled elements. Use a LinearLayout to provide dividers
5344 LinearLayout layout = new LinearLayout(mContext);
5345 layout.setOrientation(LinearLayout.VERTICAL);
5346 if (position > 0) {
5347 View dividerTop = new View(mContext);
5348 dividerTop.setBackgroundResource(
5349 android.R.drawable.divider_horizontal_bright);
5350 layout.addView(dividerTop);
5351 }
5352
5353 if (Container.OPTGROUP == c.mEnabled) {
5354 // Currently select_dialog_multichoice and
5355 // select_dialog_singlechoice are CheckedTextViews. If
5356 // that changes, the class cast will no longer be valid.
5357 Assert.assertTrue(
5358 convertView instanceof CheckedTextView);
5359 ((CheckedTextView) convertView).setCheckMarkDrawable(
5360 null);
5361 } else {
5362 // c.mEnabled == Container.OPTION_DISABLED
5363 // Draw the disabled element in a disabled state.
5364 convertView.setEnabled(false);
5365 }
5366
5367 layout.addView(convertView);
5368 if (position < getCount() - 1) {
5369 View dividerBottom = new View(mContext);
5370 dividerBottom.setBackgroundResource(
5371 android.R.drawable.divider_horizontal_bright);
5372 layout.addView(dividerBottom);
5373 }
5374 return layout;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005375 }
5376 return convertView;
5377 }
5378
5379 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005380 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04005381 // AdapterView's onChanged method uses this to determine whether
5382 // to restore the old state. Return false so that the old (out
5383 // of date) state does not replace the new, valid state.
5384 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005385 }
5386
5387 private Container item(int position) {
5388 if (position < 0 || position >= getCount()) {
5389 return null;
5390 }
5391 return (Container) getItem(position);
5392 }
5393
5394 @Override
5395 public long getItemId(int position) {
5396 Container item = item(position);
5397 if (item == null) {
5398 return -1;
5399 }
5400 return item.mId;
5401 }
5402
5403 @Override
5404 public boolean areAllItemsEnabled() {
5405 return false;
5406 }
5407
5408 @Override
5409 public boolean isEnabled(int position) {
5410 Container item = item(position);
5411 if (item == null) {
5412 return false;
5413 }
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005414 return Container.OPTION_ENABLED == item.mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005415 }
5416 }
5417
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005418 private InvokeListBox(String[] array, int[] enabled, int[] selected) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005419 mMultiple = true;
5420 mSelectedArray = selected;
5421
5422 int length = array.length;
5423 mContainers = new Container[length];
5424 for (int i = 0; i < length; i++) {
5425 mContainers[i] = new Container();
5426 mContainers[i].mString = array[i];
5427 mContainers[i].mEnabled = enabled[i];
5428 mContainers[i].mId = i;
5429 }
5430 }
5431
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005432 private InvokeListBox(String[] array, int[] enabled, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005433 mSelection = selection;
5434 mMultiple = false;
5435
5436 int length = array.length;
5437 mContainers = new Container[length];
5438 for (int i = 0; i < length; i++) {
5439 mContainers[i] = new Container();
5440 mContainers[i].mString = array[i];
5441 mContainers[i].mEnabled = enabled[i];
5442 mContainers[i].mId = i;
5443 }
5444 }
5445
Leon Scroggins3667ce42009-05-13 15:58:03 -04005446 /*
5447 * Whenever the data set changes due to filtering, this class ensures
5448 * that the checked item remains checked.
5449 */
5450 private class SingleDataSetObserver extends DataSetObserver {
5451 private long mCheckedId;
5452 private ListView mListView;
5453 private Adapter mAdapter;
5454
5455 /*
5456 * Create a new observer.
5457 * @param id The ID of the item to keep checked.
5458 * @param l ListView for getting and clearing the checked states
5459 * @param a Adapter for getting the IDs
5460 */
5461 public SingleDataSetObserver(long id, ListView l, Adapter a) {
5462 mCheckedId = id;
5463 mListView = l;
5464 mAdapter = a;
5465 }
5466
5467 public void onChanged() {
5468 // The filter may have changed which item is checked. Find the
5469 // item that the ListView thinks is checked.
5470 int position = mListView.getCheckedItemPosition();
5471 long id = mAdapter.getItemId(position);
5472 if (mCheckedId != id) {
5473 // Clear the ListView's idea of the checked item, since
5474 // it is incorrect
5475 mListView.clearChoices();
5476 // Search for mCheckedId. If it is in the filtered list,
5477 // mark it as checked
5478 int count = mAdapter.getCount();
5479 for (int i = 0; i < count; i++) {
5480 if (mAdapter.getItemId(i) == mCheckedId) {
5481 mListView.setItemChecked(i, true);
5482 break;
5483 }
5484 }
5485 }
5486 }
5487
5488 public void onInvalidate() {}
5489 }
5490
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005491 public void run() {
5492 final ListView listView = (ListView) LayoutInflater.from(mContext)
5493 .inflate(com.android.internal.R.layout.select_dialog, null);
Cary Clarkd6982c92009-05-29 11:02:22 -04005494 final MyArrayListAdapter adapter = new
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005495 MyArrayListAdapter(mContext, mContainers, mMultiple);
5496 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
5497 .setView(listView).setCancelable(true)
5498 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04005499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005500 if (mMultiple) {
5501 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
5502 public void onClick(DialogInterface dialog, int which) {
5503 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04005504 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005505 adapter.getCount(), 0,
5506 listView.getCheckedItemPositions());
5507 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07005508 b.setNegativeButton(android.R.string.cancel,
5509 new DialogInterface.OnClickListener() {
5510 public void onClick(DialogInterface dialog, int which) {
5511 mWebViewCore.sendMessage(
5512 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5513 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005514 }
5515 final AlertDialog dialog = b.create();
5516 listView.setAdapter(adapter);
5517 listView.setFocusableInTouchMode(true);
5518 // There is a bug (1250103) where the checks in a ListView with
5519 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04005520 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005521 // filtered. Do not allow filtering on multiple lists until
5522 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04005523
Leon Scroggins3667ce42009-05-13 15:58:03 -04005524 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005525 if (mMultiple) {
5526 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
5527 int length = mSelectedArray.length;
5528 for (int i = 0; i < length; i++) {
5529 listView.setItemChecked(mSelectedArray[i], true);
5530 }
5531 } else {
5532 listView.setOnItemClickListener(new OnItemClickListener() {
5533 public void onItemClick(AdapterView parent, View v,
5534 int position, long id) {
5535 mWebViewCore.sendMessage(
5536 EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
5537 dialog.dismiss();
5538 }
5539 });
5540 if (mSelection != -1) {
5541 listView.setSelection(mSelection);
5542 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
5543 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04005544 DataSetObserver observer = new SingleDataSetObserver(
5545 adapter.getItemId(mSelection), listView, adapter);
5546 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005547 }
5548 }
5549 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
5550 public void onCancel(DialogInterface dialog) {
5551 mWebViewCore.sendMessage(
5552 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5553 }
5554 });
5555 dialog.show();
5556 }
5557 }
5558
5559 /*
5560 * Request a dropdown menu for a listbox with multiple selection.
5561 *
5562 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005563 * @param enabledArray State for each element in the list. See static
5564 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005565 * @param selectedArray Which positions are initally selected.
5566 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005567 void requestListBox(String[] array, int[] enabledArray, int[]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005568 selectedArray) {
5569 mPrivateHandler.post(
5570 new InvokeListBox(array, enabledArray, selectedArray));
5571 }
5572
5573 /*
5574 * Request a dropdown menu for a listbox with single selection or a single
5575 * <select> element.
5576 *
5577 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005578 * @param enabledArray State for each element in the list. See static
5579 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005580 * @param selection Which position is initally selected.
5581 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005582 void requestListBox(String[] array, int[] enabledArray, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005583 mPrivateHandler.post(
5584 new InvokeListBox(array, enabledArray, selection));
5585 }
5586
5587 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005588 private void sendMoveMouse(int frame, int node, int x, int y) {
5589 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
5590 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005591 }
5592
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005593 /*
5594 * Send a mouse move event to the webcore thread.
5595 *
5596 * @param removeFocus Pass true if the "mouse" cursor is now over a node
5597 * which wants key events, but it is not the focus. This
5598 * will make the visual appear as though nothing is in
5599 * focus. Remove the WebTextView, if present, and stop
5600 * drawing the blinking caret.
5601 * called by JNI
5602 */
5603 private void sendMoveMouseIfLatest(boolean removeFocus) {
5604 if (removeFocus) {
5605 clearTextEntry();
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04005606 setFocusControllerInactive();
Cary Clark19436562009-06-04 16:25:07 -04005607 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005608 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
5609 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005610 }
5611
5612 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005613 private void sendMotionUp(int touchGeneration,
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005614 int frame, int node, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005615 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
5616 touchUpData.mMoveGeneration = touchGeneration;
Cary Clarkd6982c92009-05-29 11:02:22 -04005617 touchUpData.mFrame = frame;
5618 touchUpData.mNode = node;
5619 touchUpData.mX = x;
5620 touchUpData.mY = y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005621 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
5622 }
5623
5624
5625 private int getScaledMaxXScroll() {
5626 int width;
5627 if (mHeightCanMeasure == false) {
5628 width = getViewWidth() / 4;
5629 } else {
5630 Rect visRect = new Rect();
5631 calcOurVisibleRect(visRect);
5632 width = visRect.width() / 2;
5633 }
5634 // FIXME the divisor should be retrieved from somewhere
Leon Scroggins0236e672009-09-02 21:12:08 -04005635 return viewToContentX(width);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005636 }
5637
5638 private int getScaledMaxYScroll() {
5639 int height;
5640 if (mHeightCanMeasure == false) {
5641 height = getViewHeight() / 4;
5642 } else {
5643 Rect visRect = new Rect();
5644 calcOurVisibleRect(visRect);
5645 height = visRect.height() / 2;
5646 }
5647 // FIXME the divisor should be retrieved from somewhere
5648 // the closest thing today is hard-coded into ScrollView.java
5649 // (from ScrollView.java, line 363) int maxJump = height/2;
Cary Clarkdf344372009-09-15 12:47:39 -04005650 return Math.round(height * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005651 }
5652
5653 /**
5654 * Called by JNI to invalidate view
5655 */
5656 private void viewInvalidate() {
5657 invalidate();
5658 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005659
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005660 // return true if the key was handled
Cary Clark215b72c2009-06-26 14:38:43 -04005661 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
5662 long time, boolean ignorePlugin) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005663 if (mNativeClass == 0) {
5664 return false;
5665 }
Derek Sollenberger718d69f2009-10-19 15:56:43 -04005666 if (ignorePlugin == false && nativeFocusIsPlugin()) {
Cary Clark215b72c2009-06-26 14:38:43 -04005667 KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
5668 , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
5669 | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
5670 | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
5671 , 0, 0, 0);
5672 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
5673 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
5674 return true;
5675 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005676 mLastCursorTime = time;
5677 mLastCursorBounds = nativeGetCursorRingBounds();
5678 boolean keyHandled
5679 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005680 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005681 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
5682 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005683 + " handled=" + keyHandled);
5684 }
5685 if (keyHandled == false || mHeightCanMeasure == false) {
5686 return keyHandled;
5687 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005688 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
5689 if (contentCursorRingBounds.isEmpty()) return keyHandled;
Mike Reede9e86b82009-09-15 11:26:53 -04005690 Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005691 Rect visRect = new Rect();
5692 calcOurVisibleRect(visRect);
5693 Rect outset = new Rect(visRect);
5694 int maxXScroll = visRect.width() / 2;
5695 int maxYScroll = visRect.height() / 2;
5696 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005697 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005698 return keyHandled;
5699 }
5700 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005701 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
5702 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005703 if (maxH > 0) {
5704 pinScrollBy(maxH, 0, true, 0);
5705 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005706 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
5707 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005708 if (maxH < 0) {
5709 pinScrollBy(maxH, 0, true, 0);
5710 }
5711 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005712 if (mLastCursorBounds.isEmpty()) return keyHandled;
5713 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
5714 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005715 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005716 if (DebugFlags.WEB_VIEW) {
5717 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
5718 + contentCursorRingBounds);
5719 }
5720 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005721 mUserScroll = true;
5722 return keyHandled;
5723 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005724
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005725 /**
5726 * Set the background color. It's white by default. Pass
5727 * zero to make the view transparent.
5728 * @param color the ARGB color described by Color.java
5729 */
5730 public void setBackgroundColor(int color) {
5731 mBackgroundColor = color;
5732 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
5733 }
5734
5735 public void debugDump() {
5736 nativeDebugDump();
5737 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
5738 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005739
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005740 /**
Mike Reedefe2c722009-10-14 09:42:02 -04005741 * Draw the HTML page into the specified canvas. This call ignores any
5742 * view-specific zoom, scroll offset, or other changes. It does not draw
5743 * any view-specific chrome, such as progress or URL bars.
5744 *
5745 * @hide only needs to be accessible to Browser and testing
5746 */
5747 public void drawPage(Canvas canvas) {
5748 mWebViewCore.drawContentPicture(canvas, 0, false, false);
5749 }
5750
5751 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005752 * Update our cache with updatedText.
5753 * @param updatedText The new text to put in our cache.
5754 */
5755 /* package */ void updateCachedTextfield(String updatedText) {
5756 // Also place our generation number so that when we look at the cache
5757 // we recognize that it is up to date.
5758 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
5759 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005760
Leon Scroggins4890feb2009-07-02 10:37:10 -04005761 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005762 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04005763 private native int nativeCursorFramePointer();
5764 private native Rect nativeCursorNodeBounds();
5765 /* package */ native int nativeCursorNodePointer();
5766 /* package */ native boolean nativeCursorMatchesFocus();
5767 private native boolean nativeCursorIntersects(Rect visibleRect);
5768 private native boolean nativeCursorIsAnchor();
Leon Scroggins1d96ca02009-10-23 11:49:03 -04005769 private native boolean nativeCursorIsReadOnly();
Cary Clarkd6982c92009-05-29 11:02:22 -04005770 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04005771 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04005772 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005773 /**
5774 * Returns true if the native cursor node says it wants to handle key events
5775 * (ala plugins). This can only be called if mNativeClass is non-zero!
5776 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04005777 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005778 private native void nativeDebugDump();
5779 private native void nativeDestroy();
Cary Clarkd6982c92009-05-29 11:02:22 -04005780 private native void nativeDrawCursorRing(Canvas content);
5781 private native void nativeDrawMatches(Canvas canvas);
Cary Clark09e383c2009-10-26 16:43:58 -04005782 private native void nativeDrawSelectionPointer(Canvas content,
5783 float scale, int x, int y, boolean extendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005784 private native void nativeDrawSelectionRegion(Canvas content);
Cary Clarkd6982c92009-05-29 11:02:22 -04005785 private native void nativeDumpDisplayTree(String urlOrNull);
5786 private native int nativeFindAll(String findLower, String findUpper);
5787 private native void nativeFindNext(boolean forward);
Cary Clark3e88ddc2009-10-08 14:59:46 -04005788 private native int nativeFocusCandidateFramePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005789 private native boolean nativeFocusCandidateIsPassword();
Cary Clark3e88ddc2009-10-08 14:59:46 -04005790 private native boolean nativeFocusCandidateIsPlugin();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005791 private native boolean nativeFocusCandidateIsRtlText();
5792 private native boolean nativeFocusCandidateIsTextField();
5793 private native boolean nativeFocusCandidateIsTextInput();
5794 private native int nativeFocusCandidateMaxLength();
Leon Scroggins0ca70882009-06-26 17:45:29 -04005795 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005796 private native Rect nativeFocusCandidateNodeBounds();
5797 /* package */ native int nativeFocusCandidatePointer();
5798 private native String nativeFocusCandidateText();
5799 private native int nativeFocusCandidateTextSize();
Derek Sollenberger718d69f2009-10-19 15:56:43 -04005800 private native boolean nativeFocusIsPlugin();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005801 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04005802 private native Rect nativeGetCursorRingBounds();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005803 private native Region nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04005804 private native boolean nativeHasCursorNode();
5805 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04005806 private native void nativeHideCursor();
Cary Clarkd6982c92009-05-29 11:02:22 -04005807 private native String nativeImageURI(int x, int y);
5808 private native void nativeInstrumentReport();
Leon Scroggins01058282009-07-30 16:33:56 -04005809 /* package */ native void nativeMoveCursorToNextTextInput();
Cary Clarkd6982c92009-05-29 11:02:22 -04005810 // return true if the page has been scrolled
5811 private native boolean nativeMotionUp(int x, int y, int slop);
5812 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005813 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04005814 boolean noScroll);
5815 private native int nativeMoveGeneration();
5816 private native void nativeMoveSelection(int x, int y,
5817 boolean extendSelection);
Cary Clarkd6982c92009-05-29 11:02:22 -04005818 // Like many other of our native methods, you must make sure that
5819 // mNativeClass is not null before calling this method.
5820 private native void nativeRecordButtons(boolean focused,
5821 boolean pressed, boolean invalidate);
5822 private native void nativeSelectBestAt(Rect rect);
5823 private native void nativeSetFindIsDown();
5824 private native void nativeSetFollowedLink(boolean followed);
5825 private native void nativeSetHeightCanMeasure(boolean measure);
Leon Scroggins01058282009-07-30 16:33:56 -04005826 // Returns a value corresponding to CachedFrame::ImeAction
5827 /* package */ native int nativeTextFieldAction();
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005828 /**
5829 * Perform a click on a currently focused text input. Since it is already
5830 * focused, there is no need to go through the nativeMotionUp code, which
5831 * may change the Cursor.
5832 */
5833 private native void nativeTextInputMotionUp(int x, int y);
Cary Clarkd6982c92009-05-29 11:02:22 -04005834 private native int nativeTextGeneration();
5835 // Never call this version except by updateCachedTextfield(String) -
5836 // we always want to pass in our generation number.
5837 private native void nativeUpdateCachedTextfield(String updatedText,
5838 int generation);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005839 // return NO_LEFTEDGE means failure.
5840 private static final int NO_LEFTEDGE = -1;
Cary Clark77d98f42009-07-31 09:40:38 -04005841 private native int nativeGetBlockLeftEdge(int x, int y, float scale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005842}