blob: 28e9b6d566b5dd0be6ddd4ca4261ca5de56ce131 [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;
28import android.graphics.Paint;
29import android.graphics.Path;
30import android.graphics.Picture;
31import android.graphics.Point;
32import android.graphics.Rect;
33import android.graphics.Region;
34import android.net.http.SslCertificate;
35import android.net.Uri;
36import android.os.Bundle;
37import android.os.Handler;
38import android.os.Message;
39import android.os.ServiceManager;
40import android.os.SystemClock;
41import android.provider.Checkin;
42import android.text.IClipboard;
43import android.text.Selection;
44import android.text.Spannable;
45import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.util.EventLog;
47import android.util.Log;
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;
The Android Open Source Project10592532009-03-18 17:39:46 -070067import android.widget.FrameLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.widget.ImageView;
69import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.widget.Scroller;
71import android.widget.Toast;
72import android.widget.ZoomButtonsController;
The Android Open Source Project10592532009-03-18 17:39:46 -070073import android.widget.ZoomControls;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import android.widget.AdapterView.OnItemClickListener;
75
76import java.io.File;
77import java.io.FileInputStream;
78import java.io.FileNotFoundException;
79import java.io.FileOutputStream;
80import java.io.IOException;
81import java.net.URLDecoder;
82import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083import java.util.List;
84
85/**
Cary Clarkd6982c92009-05-29 11:02:22 -040086 * <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 -080087 * can roll your own web browser or simply display some online content within your Activity.
88 * It uses the WebKit rendering engine to display
89 * web pages and includes methods to navigate forward and backward
90 * through a history, zoom in and out, perform text searches and more.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -070091 * <p>To enable the built-in zoom, set
92 * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
93 * (introduced in API version 3).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 * <p>Note that, in order for your Activity to access the Internet and load web pages
Cary Clarkd6982c92009-05-29 11:02:22 -040095 * in a WebView, you must add the <var>INTERNET</var> permissions to your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 * Android Manifest file:</p>
97 * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +010098 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 * <p>This must be a child of the <code>&lt;manifest></code> element.</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100100 *
101 * <h3>Basic usage</h3>
102 *
103 * <p>By default, a WebView provides no browser-like widgets, does not
104 * enable JavaScript and errors will be ignored. If your goal is only
105 * to display some HTML as a part of your UI, this is probably fine;
106 * the user won't need to interact with the web page beyond reading
107 * it, and the web page won't need to interact with the user. If you
108 * actually want a fully blown web browser, then you probably want to
109 * invoke the Browser application with your URL rather than show it
110 * with a WebView. See {@link android.content.Intent} for more information.</p>
111 *
112 * <pre class="prettyprint">
113 * WebView webview = new WebView(this);
114 * setContentView(webview);
115 *
116 * // Simplest usage: note that an exception will NOT be thrown
117 * // if there is an error loading this page (see below).
118 * webview.loadUrl("http://slashdot.org/");
119 *
120 * // Of course you can also load from any string:
121 * String summary = "&lt;html>&lt;body>You scored &lt;b>192</b> points.&lt;/body>&lt;/html>";
122 * webview.loadData(summary, "text/html", "utf-8");
123 * // ... although note that there are restrictions on what this HTML can do.
124 * // See the JavaDocs for loadData and loadDataWithBaseUrl for more info.
125 * </pre>
126 *
127 * <p>A WebView has several customization points where you can add your
128 * own behavior. These are:</p>
129 *
130 * <ul>
131 * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
132 * This class is called when something that might impact a
133 * browser UI happens, for instance, progress updates and
134 * JavaScript alerts are sent here.
135 * </li>
136 * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
137 * It will be called when things happen that impact the
138 * rendering of the content, eg, errors or form submissions. You
139 * can also intercept URL loading here.</li>
140 * <li>Via the {@link android.webkit.WebSettings} class, which contains
141 * miscellaneous configuration. </li>
142 * <li>With the {@link android.webkit.WebView#addJavascriptInterface} method.
143 * This lets you bind Java objects into the WebView so they can be
144 * controlled from the web pages JavaScript.</li>
145 * </ul>
146 *
147 * <p>Here's a more complicated example, showing error handling,
148 * settings, and progress notification:</p>
149 *
150 * <pre class="prettyprint">
151 * // Let's display the progress in the activity title bar, like the
152 * // browser app does.
153 * getWindow().requestFeature(Window.FEATURE_PROGRESS);
154 *
155 * webview.getSettings().setJavaScriptEnabled(true);
156 *
157 * final Activity activity = this;
158 * webview.setWebChromeClient(new WebChromeClient() {
159 * public void onProgressChanged(WebView view, int progress) {
160 * // Activities and WebViews measure progress with different scales.
161 * // The progress meter will automatically disappear when we reach 100%
162 * activity.setProgress(progress * 1000);
163 * }
164 * });
165 * webview.setWebViewClient(new WebViewClient() {
166 * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
167 * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
168 * }
169 * });
170 *
171 * webview.loadUrl("http://slashdot.org/");
172 * </pre>
173 *
174 * <h3>Cookie and window management</h3>
175 *
176 * <p>For obvious security reasons, your application has its own
177 * cache, cookie store etc - it does not share the Browser
178 * applications data. Cookies are managed on a separate thread, so
179 * operations like index building don't block the UI
180 * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
181 * if you want to use cookies in your application.
182 * </p>
183 *
184 * <p>By default, requests by the HTML to open new windows are
185 * ignored. This is true whether they be opened by JavaScript or by
186 * the target attribute on a link. You can customize your
187 * WebChromeClient to provide your own behaviour for opening multiple windows,
188 * and render them in whatever manner you want.</p>
189 *
190 * <p>Standard behavior for an Activity is to be destroyed and
191 * recreated when the devices orientation is changed. This will cause
192 * the WebView to reload the current page. If you don't want that, you
193 * can set your Activity to handle the orientation and keyboardHidden
194 * changes, and then just leave the WebView alone. It'll automatically
195 * re-orient itself as appropriate.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 */
Cary Clarkd6982c92009-05-29 11:02:22 -0400197public class WebView extends AbsoluteLayout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 implements ViewTreeObserver.OnGlobalFocusChangeListener,
199 ViewGroup.OnHierarchyChangeListener {
200
201 // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
202 // the screen all-the-time. Good for profiling our drawing code
203 static private final boolean AUTO_REDRAW_HACK = false;
204 // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
205 private boolean mAutoRedraw;
206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 static final String LOGTAG = "webview";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208
Leon Scroggins213a31c2009-05-21 16:49:32 -0700209 private static class ExtendedZoomControls extends FrameLayout {
The Android Open Source Project10592532009-03-18 17:39:46 -0700210 public ExtendedZoomControls(Context context, AttributeSet attrs) {
211 super(context, attrs);
212 LayoutInflater inflater = (LayoutInflater)
213 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
214 inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400215 mPlusMinusZoomControls = (ZoomControls) findViewById(
216 com.android.internal.R.id.zoomControls);
The Android Open Source Project10592532009-03-18 17:39:46 -0700217 mZoomMagnify = (ImageView) findViewById(com.android.internal.R.id.zoomMagnify);
218 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400219
The Android Open Source Project10592532009-03-18 17:39:46 -0700220 public void show(boolean showZoom, boolean canZoomOut) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400221 mPlusMinusZoomControls.setVisibility(
222 showZoom ? View.VISIBLE : View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700223 mZoomMagnify.setVisibility(canZoomOut ? View.VISIBLE : View.GONE);
224 fade(View.VISIBLE, 0.0f, 1.0f);
225 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400226
The Android Open Source Project10592532009-03-18 17:39:46 -0700227 public void hide() {
228 fade(View.GONE, 1.0f, 0.0f);
229 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400230
The Android Open Source Project10592532009-03-18 17:39:46 -0700231 private void fade(int visibility, float startAlpha, float endAlpha) {
232 AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
233 anim.setDuration(500);
234 startAnimation(anim);
235 setVisibility(visibility);
236 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400237
The Android Open Source Project10592532009-03-18 17:39:46 -0700238 public void setIsZoomMagnifyEnabled(boolean isEnabled) {
239 mZoomMagnify.setEnabled(isEnabled);
240 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400241
The Android Open Source Project10592532009-03-18 17:39:46 -0700242 public boolean hasFocus() {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400243 return mPlusMinusZoomControls.hasFocus() || mZoomMagnify.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
The Android Open Source Project10592532009-03-18 17:39:46 -0700254 public void setOnZoomMagnifyClickListener(OnClickListener listener) {
255 mZoomMagnify.setOnClickListener(listener);
256 }
257
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400258 ZoomControls mPlusMinusZoomControls;
259 ImageView mZoomMagnify;
The Android Open Source Project10592532009-03-18 17:39:46 -0700260 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400261
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262 /**
263 * Transportation object for returning WebView across thread boundaries.
264 */
265 public class WebViewTransport {
266 private WebView mWebview;
267
268 /**
269 * Set the WebView to the transportation object.
270 * @param webview The WebView to transport.
271 */
272 public synchronized void setWebView(WebView webview) {
273 mWebview = webview;
274 }
275
276 /**
277 * Return the WebView object.
278 * @return WebView The transported WebView object.
279 */
280 public synchronized WebView getWebView() {
281 return mWebview;
282 }
283 }
284
285 // A final CallbackProxy shared by WebViewCore and BrowserFrame.
286 private final CallbackProxy mCallbackProxy;
287
288 private final WebViewDatabase mDatabase;
289
290 // SSL certificate for the main top-level page (if secure)
291 private SslCertificate mCertificate;
292
293 // Native WebView pointer that is 0 until the native object has been
294 // created.
295 private int mNativeClass;
296 // This would be final but it needs to be set to null when the WebView is
297 // destroyed.
298 private WebViewCore mWebViewCore;
299 // Handler for dispatching UI messages.
300 /* package */ final Handler mPrivateHandler = new PrivateHandler();
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400301 private WebTextView mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 // Used to ignore changes to webkit text that arrives to the UI side after
303 // more key events.
304 private int mTextGeneration;
305
Patrick Scott0a5ce012009-07-02 08:56:10 -0400306 // Used by WebViewCore to create child views.
307 /* package */ final ViewManager mViewManager;
308
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800309 /**
310 * Position of the last touch event.
311 */
312 private float mLastTouchX;
313 private float mLastTouchY;
314
315 /**
316 * Time of the last touch event.
317 */
318 private long mLastTouchTime;
319
320 /**
321 * Time of the last time sending touch event to WebViewCore
322 */
323 private long mLastSentTouchTime;
324
325 /**
326 * The minimum elapsed time before sending another ACTION_MOVE event to
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700327 * WebViewCore. This really should be tuned for each type of the devices.
328 * For example in Google Map api test case, it takes Dream device at least
329 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
330 * triggering the layout and drawing the picture. While the same process
331 * takes 60+ms on the current high speed device. If we make
332 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
333 * to WebViewCore queue and the real layout and draw events will be pushed
334 * to further, which slows down the refresh rate. Choose 50 to favor the
335 * current high speed devices. For Dream like devices, 100 is a better
336 * choice. Maybe make this in the buildspec later.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 */
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700338 private static final int TOUCH_SENT_INTERVAL = 50;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339
340 /**
341 * Helper class to get velocity for fling
342 */
343 VelocityTracker mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700344 private int mMaximumFling;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700346 // use this flag to control whether enabling the new double tap zoom
347 static final boolean ENABLE_DOUBLETAP_ZOOM = true;
348
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 /**
350 * Touch mode
351 */
352 private int mTouchMode = TOUCH_DONE_MODE;
353 private static final int TOUCH_INIT_MODE = 1;
354 private static final int TOUCH_DRAG_START_MODE = 2;
355 private static final int TOUCH_DRAG_MODE = 3;
356 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
357 private static final int TOUCH_SHORTPRESS_MODE = 5;
358 private static final int TOUCH_DOUBLECLICK_MODE = 6;
359 private static final int TOUCH_DONE_MODE = 7;
360 private static final int TOUCH_SELECT_MODE = 8;
361 // touch mode values specific to scale+scroll
362 private static final int FIRST_SCROLL_ZOOM = 9;
363 private static final int SCROLL_ZOOM_ANIMATION_IN = 9;
364 private static final int SCROLL_ZOOM_ANIMATION_OUT = 10;
365 private static final int SCROLL_ZOOM_OUT = 11;
366 private static final int LAST_SCROLL_ZOOM = 11;
367 // end of touch mode values specific to scale+scroll
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700368 private static final int TOUCH_DOUBLE_TAP_MODE = 12;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369
370 // Whether to forward the touch events to WebCore
371 private boolean mForwardTouchEvents = false;
372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373 // Whether to prevent drag during touch. The initial value depends on
374 // mForwardTouchEvents. If WebCore wants touch events, we assume it will
375 // take control of touch events unless it says no for touch down event.
376 private boolean mPreventDrag;
377
Leon Scroggins72543e12009-07-23 15:29:45 -0400378 // To keep track of whether the current drag was initiated by a WebTextView,
379 // so that we know not to hide the cursor
380 boolean mDragFromTextInput;
381
Cary Clarkd6982c92009-05-29 11:02:22 -0400382 // Whether or not to draw the cursor ring.
383 private boolean mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384
Mike Reedd205d5b2009-05-27 11:02:29 -0400385 // true if onPause has been called (and not onResume)
386 private boolean mIsPaused;
387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 /**
389 * Customizable constant
390 */
391 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
392 private int mTouchSlopSquare;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700393 // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
394 private int mDoubleTapSlopSquare;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700395 // pre-computed density adjusted navigation slop
396 private int mNavSlop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 // This should be ViewConfiguration.getTapTimeout()
398 // But system time out is 100ms, which is too short for the browser.
399 // In the browser, if it switches out of tap too soon, jump tap won't work.
400 private static final int TAP_TIMEOUT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800401 // This should be ViewConfiguration.getLongPressTimeout()
402 // But system time out is 500ms, which is too short for the browser.
403 // With a short timeout, it's difficult to treat trigger a short press.
404 private static final int LONG_PRESS_TIMEOUT = 1000;
405 // needed to avoid flinging after a pause of no movement
406 private static final int MIN_FLING_TIME = 250;
The Android Open Source Project10592532009-03-18 17:39:46 -0700407 // The time that the Zoom Controls are visible before fading away
Cary Clarkd6982c92009-05-29 11:02:22 -0400408 private static final long ZOOM_CONTROLS_TIMEOUT =
The Android Open Source Project10592532009-03-18 17:39:46 -0700409 ViewConfiguration.getZoomControlsTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800410 // The amount of content to overlap between two screens when going through
411 // pages with the space bar, in pixels.
412 private static final int PAGE_SCROLL_OVERLAP = 24;
413
414 /**
415 * These prevent calling requestLayout if either dimension is fixed. This
416 * depends on the layout parameters and the measure specs.
417 */
418 boolean mWidthCanMeasure;
419 boolean mHeightCanMeasure;
420
421 // Remember the last dimensions we sent to the native side so we can avoid
422 // sending the same dimensions more than once.
423 int mLastWidthSent;
424 int mLastHeightSent;
425
426 private int mContentWidth; // cache of value from WebViewCore
427 private int mContentHeight; // cache of value from WebViewCore
428
Cary Clarkd6982c92009-05-29 11:02:22 -0400429 // Need to have the separate control for horizontal and vertical scrollbar
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 // style than the View's single scrollbar style
431 private boolean mOverlayHorizontalScrollbar = true;
432 private boolean mOverlayVerticalScrollbar = false;
433
434 // our standard speed. this way small distances will be traversed in less
435 // time than large distances, but we cap the duration, so that very large
436 // distances won't take too long to get there.
437 private static final int STD_SPEED = 480; // pixels per second
438 // time for the longest scroll animation
439 private static final int MAX_DURATION = 750; // milliseconds
440 private Scroller mScroller;
441
442 private boolean mWrapContent;
443
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 /**
445 * Private message ids
446 */
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400447 private static final int REMEMBER_PASSWORD = 1;
448 private static final int NEVER_REMEMBER_PASSWORD = 2;
449 private static final int SWITCH_TO_SHORTPRESS = 3;
450 private static final int SWITCH_TO_LONGPRESS = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700451 private static final int RELEASE_SINGLE_TAP = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400452 private static final int REQUEST_FORM_DATA = 6;
453 private static final int SWITCH_TO_CLICK = 7;
454 private static final int RESUME_WEBCORE_UPDATE = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800455
456 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400457 static final int SCROLL_TO_MSG_ID = 10;
458 static final int SCROLL_BY_MSG_ID = 11;
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 SPAWN_SCROLL_TO_MSG_ID = 12;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800461 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400462 static final int SYNC_SCROLL_TO_MSG_ID = 13;
463 static final int NEW_PICTURE_MSG_ID = 14;
464 static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
465 static final int WEBCORE_INITIALIZED_MSG_ID = 16;
466 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
Cary Clark215b72c2009-06-26 14:38:43 -0400467 static final int MOVE_OUT_OF_PLUGIN = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400468 static final int CLEAR_TEXT_ENTRY = 20;
Leon Scroggins6679f2f2009-08-12 18:48:10 -0400469 static final int UPDATE_TEXT_SELECTION_MSG_ID = 21;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400470 static final int UPDATE_CLIPBOARD = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400471 static final int LONG_PRESS_CENTER = 23;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400472 static final int PREVENT_TOUCH_ID = 24;
473 static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800474 // obj=Rect in doc coordinates
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400475 static final int INVAL_RECT_MSG_ID = 26;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400476 static final int REQUEST_KEYBOARD = 27;
Cary Clarkd6982c92009-05-29 11:02:22 -0400477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 static final String[] HandlerDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400479 "REMEMBER_PASSWORD", // = 1;
480 "NEVER_REMEMBER_PASSWORD", // = 2;
481 "SWITCH_TO_SHORTPRESS", // = 3;
482 "SWITCH_TO_LONGPRESS", // = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700483 "RELEASE_SINGLE_TAP", // = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400484 "REQUEST_FORM_DATA", // = 6;
485 "SWITCH_TO_CLICK", // = 7;
486 "RESUME_WEBCORE_UPDATE", // = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 "9",
488 "SCROLL_TO_MSG_ID", // = 10;
489 "SCROLL_BY_MSG_ID", // = 11;
490 "SPAWN_SCROLL_TO_MSG_ID", // = 12;
491 "SYNC_SCROLL_TO_MSG_ID", // = 13;
492 "NEW_PICTURE_MSG_ID", // = 14;
493 "UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
494 "WEBCORE_INITIALIZED_MSG_ID", // = 16;
495 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
Grace Klobaef347ef2009-07-30 11:20:32 -0700496 "18", // = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400497 "MOVE_OUT_OF_PLUGIN", // = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400498 "CLEAR_TEXT_ENTRY", // = 20;
Leon Scroggins6679f2f2009-08-12 18:48:10 -0400499 "UPDATE_TEXT_SELECTION_MSG_ID", // = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 "UPDATE_CLIPBOARD", // = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400501 "LONG_PRESS_CENTER", // = 23;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800502 "PREVENT_TOUCH_ID", // = 24;
503 "WEBCORE_NEED_TOUCH_EVENTS", // = 25;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400504 "INVAL_RECT_MSG_ID", // = 26;
505 "REQUEST_KEYBOARD" // = 27;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 };
507
Grace Kloba25737912009-06-19 12:42:47 -0700508 // default scale limit. Depending on the display density
509 private static float DEFAULT_MAX_ZOOM_SCALE;
510 private static float DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 // scale limit, which can be set through viewport meta tag in the web page
Grace Kloba25737912009-06-19 12:42:47 -0700512 private float mMaxZoomScale;
513 private float mMinZoomScale;
Grace Klobae397a882009-08-06 12:04:14 -0700514 private boolean mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800515
516 // initial scale in percent. 0 means using default.
517 private int mInitialScale = 0;
518
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700519 // while in the zoom overview mode, the page's width is fully fit to the
520 // current window. The page is alive, in another words, you can click to
521 // follow the links. Double tap will toggle between zoom overview mode and
522 // the last zoom scale.
523 boolean mInZoomOverview = false;
524 // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
525 // engadget always have wider mContentWidth no matter what viewport size is.
Grace Klobae397a882009-08-06 12:04:14 -0700526 int mZoomOverviewWidth = WebViewCore.DEFAULT_VIEWPORT_WIDTH;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700527 float mLastScale;
528
Grace Kloba25737912009-06-19 12:42:47 -0700529 // default scale. Depending on the display density.
530 static int DEFAULT_SCALE_PERCENT;
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700531 private float mDefaultScale;
Grace Kloba25737912009-06-19 12:42:47 -0700532
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800533 // set to true temporarily while the zoom control is being dragged
534 private boolean mPreviewZoomOnly = false;
535
536 // computed scale and inverse, from mZoomWidth.
Grace Kloba25737912009-06-19 12:42:47 -0700537 private float mActualScale;
538 private float mInvActualScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 // if this is non-zero, it is used on drawing rather than mActualScale
540 private float mZoomScale;
541 private float mInvInitialZoomScale;
542 private float mInvFinalZoomScale;
Grace Kloba675c7d22009-07-23 09:21:21 -0700543 private int mInitialScrollX;
544 private int mInitialScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 private long mZoomStart;
546 private static final int ZOOM_ANIMATION_LENGTH = 500;
547
548 private boolean mUserScroll = false;
549
550 private int mSnapScrollMode = SNAP_NONE;
551 private static final int SNAP_NONE = 1;
552 private static final int SNAP_X = 2;
553 private static final int SNAP_Y = 3;
554 private static final int SNAP_X_LOCK = 4;
555 private static final int SNAP_Y_LOCK = 5;
556 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400557
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 // Used to match key downs and key ups
559 private boolean mGotKeyDown;
560
561 /* package */ static boolean mLogEvent = true;
562 private static final int EVENT_LOG_ZOOM_LEVEL_CHANGE = 70101;
563 private static final int EVENT_LOG_DOUBLE_TAP_DURATION = 70102;
564
565 // for event log
566 private long mLastTouchUpTime = 0;
567
568 /**
569 * URI scheme for telephone number
570 */
571 public static final String SCHEME_TEL = "tel:";
572 /**
573 * URI scheme for email address
574 */
575 public static final String SCHEME_MAILTO = "mailto:";
576 /**
577 * URI scheme for map address
578 */
579 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400580
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 private int mBackgroundColor = Color.WHITE;
582
583 // Used to notify listeners of a new picture.
584 private PictureListener mPictureListener;
585 /**
586 * Interface to listen for new pictures as they change.
587 */
588 public interface PictureListener {
589 /**
590 * Notify the listener that the picture has changed.
591 * @param view The WebView that owns the picture.
592 * @param picture The new picture.
593 */
594 public void onNewPicture(WebView view, Picture picture);
595 }
596
Leon Scroggins3246c222009-05-26 09:51:23 -0400597 // FIXME: Want to make this public, but need to change the API file.
598 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 /**
600 * Default HitTestResult, where the target is unknown
601 */
602 public static final int UNKNOWN_TYPE = 0;
603 /**
604 * HitTestResult for hitting a HTML::a tag
605 */
606 public static final int ANCHOR_TYPE = 1;
607 /**
608 * HitTestResult for hitting a phone number
609 */
610 public static final int PHONE_TYPE = 2;
611 /**
612 * HitTestResult for hitting a map address
613 */
614 public static final int GEO_TYPE = 3;
615 /**
616 * HitTestResult for hitting an email address
617 */
618 public static final int EMAIL_TYPE = 4;
619 /**
620 * HitTestResult for hitting an HTML::img tag
621 */
622 public static final int IMAGE_TYPE = 5;
623 /**
624 * HitTestResult for hitting a HTML::a tag which contains HTML::img
625 */
626 public static final int IMAGE_ANCHOR_TYPE = 6;
627 /**
628 * HitTestResult for hitting a HTML::a tag with src=http
629 */
630 public static final int SRC_ANCHOR_TYPE = 7;
631 /**
632 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
633 */
634 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
635 /**
636 * HitTestResult for hitting an edit text area
637 */
638 public static final int EDIT_TEXT_TYPE = 9;
639
640 private int mType;
641 private String mExtra;
642
643 HitTestResult() {
644 mType = UNKNOWN_TYPE;
645 }
646
647 private void setType(int type) {
648 mType = type;
649 }
650
651 private void setExtra(String extra) {
652 mExtra = extra;
653 }
654
655 public int getType() {
656 return mType;
657 }
658
659 public String getExtra() {
660 return mExtra;
661 }
662 }
663
The Android Open Source Project10592532009-03-18 17:39:46 -0700664 // The View containing the zoom controls
665 private ExtendedZoomControls mZoomControls;
666 private Runnable mZoomControlRunnable;
667
Cary Clarkd6982c92009-05-29 11:02:22 -0400668 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700670 // These keep track of the center point of the zoom. They are used to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 // determine the point around which we should zoom.
672 private float mZoomCenterX;
673 private float mZoomCenterY;
674
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700675 private ZoomButtonsController.OnZoomListener mZoomListener =
676 new ZoomButtonsController.OnZoomListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 public void onVisibilityChanged(boolean visible) {
679 if (visible) {
680 switchOutDrawHistory();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700681 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 }
683 }
684
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700685 public void onZoom(boolean zoomIn) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800686 if (zoomIn) {
687 zoomIn();
688 } else {
689 zoomOut();
690 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400691
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700692 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800693 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 };
Cary Clarkd6982c92009-05-29 11:02:22 -0400695
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 /**
697 * Construct a new WebView with a Context object.
698 * @param context A Context object used to access application assets.
699 */
700 public WebView(Context context) {
701 this(context, null);
702 }
703
704 /**
705 * Construct a new WebView with layout parameters.
706 * @param context A Context object used to access application assets.
707 * @param attrs An AttributeSet passed to our parent.
708 */
709 public WebView(Context context, AttributeSet attrs) {
710 this(context, attrs, com.android.internal.R.attr.webViewStyle);
711 }
712
713 /**
714 * Construct a new WebView with layout parameters and a default style.
715 * @param context A Context object used to access application assets.
716 * @param attrs An AttributeSet passed to our parent.
717 * @param defStyle The default style resource ID.
718 */
719 public WebView(Context context, AttributeSet attrs, int defStyle) {
720 super(context, attrs, defStyle);
721 init();
722
723 mCallbackProxy = new CallbackProxy(context, this);
724 mWebViewCore = new WebViewCore(context, this, mCallbackProxy);
725 mDatabase = WebViewDatabase.getInstance(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 mScroller = new Scroller(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700727
Patrick Scott0a5ce012009-07-02 08:56:10 -0400728 mViewManager = new ViewManager(this);
729
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700730 mZoomButtonsController = new ZoomButtonsController(this);
731 mZoomButtonsController.setOnZoomListener(mZoomListener);
Leon Scrogginsaa3f96a2009-06-11 14:46:35 -0400732 // ZoomButtonsController positions the buttons at the bottom, but in
733 // the middle. Change their layout parameters so they appear on the
734 // right.
735 View controls = mZoomButtonsController.getZoomControls();
736 ViewGroup.LayoutParams params = controls.getLayoutParams();
737 if (params instanceof FrameLayout.LayoutParams) {
738 FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
739 params;
740 frameParams.gravity = Gravity.RIGHT;
741 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700742 }
743
744 private void updateZoomButtonsEnabled() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700745 boolean canZoomIn = mActualScale < mMaxZoomScale;
746 boolean canZoomOut = mActualScale > mMinZoomScale;
747 if (!canZoomIn && !canZoomOut) {
748 // Hide the zoom in and out buttons, as well as the fit to page
749 // button, if the page cannot zoom
750 mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700751 } else {
752 // Bring back the hidden zoom controls.
753 mZoomButtonsController.getZoomControls()
754 .setVisibility(View.VISIBLE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700755 // Set each one individually, as a page may be able to zoom in
756 // or out.
757 mZoomButtonsController.setZoomInEnabled(canZoomIn);
758 mZoomButtonsController.setZoomOutEnabled(canZoomOut);
The Android Open Source Project10592532009-03-18 17:39:46 -0700759 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 }
761
762 private void init() {
763 setWillNotDraw(false);
764 setFocusable(true);
765 setFocusableInTouchMode(true);
766 setClickable(true);
767 setLongClickable(true);
768
Romain Guy4296fc42009-07-06 11:48:52 -0700769 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700770 int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 mTouchSlopSquare = slop * slop;
772 mMinLockSnapReverseDistance = slop;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700773 slop = configuration.getScaledDoubleTapSlop();
774 mDoubleTapSlopSquare = slop * slop;
Grace Kloba25737912009-06-19 12:42:47 -0700775 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700776 // use one line height, 16 based on our current default font, for how
777 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -0700778 mNavSlop = (int) (16 * density);
779 // density adjusted scale factors
780 DEFAULT_SCALE_PERCENT = (int) (100 * density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700781 mDefaultScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700782 mActualScale = density;
783 mInvActualScale = 1 / density;
784 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
785 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
786 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
787 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
Romain Guy4296fc42009-07-06 11:48:52 -0700788 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 }
790
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700791 /* package */void updateDefaultZoomDensity(int zoomDensity) {
792 final float density = getContext().getResources().getDisplayMetrics().density
793 * 100 / zoomDensity;
794 if (Math.abs(density - mDefaultScale) > 0.01) {
795 float scaleFactor = density / mDefaultScale;
796 // adjust the limits
797 mNavSlop = (int) (16 * density);
798 DEFAULT_SCALE_PERCENT = (int) (100 * density);
799 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
800 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
801 mDefaultScale = density;
802 mMaxZoomScale *= scaleFactor;
803 mMinZoomScale *= scaleFactor;
804 setNewZoomScale(mActualScale * scaleFactor, false);
805 }
806 }
807
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 /* package */ boolean onSavePassword(String schemePlusHost, String username,
809 String password, final Message resumeMsg) {
810 boolean rVal = false;
811 if (resumeMsg == null) {
812 // null resumeMsg implies saving password silently
813 mDatabase.setUsernamePassword(schemePlusHost, username, password);
814 } else {
815 final Message remember = mPrivateHandler.obtainMessage(
816 REMEMBER_PASSWORD);
817 remember.getData().putString("host", schemePlusHost);
818 remember.getData().putString("username", username);
819 remember.getData().putString("password", password);
820 remember.obj = resumeMsg;
821
822 final Message neverRemember = mPrivateHandler.obtainMessage(
823 NEVER_REMEMBER_PASSWORD);
824 neverRemember.getData().putString("host", schemePlusHost);
825 neverRemember.getData().putString("username", username);
826 neverRemember.getData().putString("password", password);
827 neverRemember.obj = resumeMsg;
828
829 new AlertDialog.Builder(getContext())
830 .setTitle(com.android.internal.R.string.save_password_label)
831 .setMessage(com.android.internal.R.string.save_password_message)
832 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
833 new DialogInterface.OnClickListener() {
834 public void onClick(DialogInterface dialog, int which) {
835 resumeMsg.sendToTarget();
836 }
837 })
838 .setNeutralButton(com.android.internal.R.string.save_password_remember,
839 new DialogInterface.OnClickListener() {
840 public void onClick(DialogInterface dialog, int which) {
841 remember.sendToTarget();
842 }
843 })
844 .setNegativeButton(com.android.internal.R.string.save_password_never,
845 new DialogInterface.OnClickListener() {
846 public void onClick(DialogInterface dialog, int which) {
847 neverRemember.sendToTarget();
848 }
849 })
850 .setOnCancelListener(new OnCancelListener() {
851 public void onCancel(DialogInterface dialog) {
852 resumeMsg.sendToTarget();
853 }
854 }).show();
855 // Return true so that WebViewCore will pause while the dialog is
856 // up.
857 rVal = true;
858 }
859 return rVal;
860 }
861
862 @Override
863 public void setScrollBarStyle(int style) {
864 if (style == View.SCROLLBARS_INSIDE_INSET
865 || style == View.SCROLLBARS_OUTSIDE_INSET) {
866 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
867 } else {
868 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
869 }
870 super.setScrollBarStyle(style);
871 }
872
873 /**
874 * Specify whether the horizontal scrollbar has overlay style.
875 * @param overlay TRUE if horizontal scrollbar should have overlay style.
876 */
877 public void setHorizontalScrollbarOverlay(boolean overlay) {
878 mOverlayHorizontalScrollbar = overlay;
879 }
880
881 /**
882 * Specify whether the vertical scrollbar has overlay style.
883 * @param overlay TRUE if vertical scrollbar should have overlay style.
884 */
885 public void setVerticalScrollbarOverlay(boolean overlay) {
886 mOverlayVerticalScrollbar = overlay;
887 }
888
889 /**
890 * Return whether horizontal scrollbar has overlay style
891 * @return TRUE if horizontal scrollbar has overlay style.
892 */
893 public boolean overlayHorizontalScrollbar() {
894 return mOverlayHorizontalScrollbar;
895 }
896
897 /**
898 * Return whether vertical scrollbar has overlay style
899 * @return TRUE if vertical scrollbar has overlay style.
900 */
901 public boolean overlayVerticalScrollbar() {
902 return mOverlayVerticalScrollbar;
903 }
904
905 /*
906 * Return the width of the view where the content of WebView should render
907 * to.
908 */
909 private int getViewWidth() {
910 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
911 return getWidth();
912 } else {
913 return getWidth() - getVerticalScrollbarWidth();
914 }
915 }
916
917 /*
918 * Return the height of the view where the content of WebView should render
919 * to.
920 */
921 private int getViewHeight() {
922 if (!isHorizontalScrollBarEnabled() || mOverlayHorizontalScrollbar) {
923 return getHeight();
924 } else {
925 return getHeight() - getHorizontalScrollbarHeight();
926 }
927 }
928
929 /**
930 * @return The SSL certificate for the main top-level page or null if
931 * there is no certificate (the site is not secure).
932 */
933 public SslCertificate getCertificate() {
934 return mCertificate;
935 }
936
937 /**
938 * Sets the SSL certificate for the main top-level page.
939 */
940 public void setCertificate(SslCertificate certificate) {
941 // here, the certificate can be null (if the site is not secure)
942 mCertificate = certificate;
943 }
944
945 //-------------------------------------------------------------------------
946 // Methods called by activity
947 //-------------------------------------------------------------------------
948
949 /**
950 * Save the username and password for a particular host in the WebView's
951 * internal database.
952 * @param host The host that required the credentials.
953 * @param username The username for the given host.
954 * @param password The password for the given host.
955 */
956 public void savePassword(String host, String username, String password) {
957 mDatabase.setUsernamePassword(host, username, password);
958 }
959
960 /**
961 * Set the HTTP authentication credentials for a given host and realm.
962 *
963 * @param host The host for the credentials.
964 * @param realm The realm for the credentials.
965 * @param username The username for the password. If it is null, it means
966 * password can't be saved.
967 * @param password The password
968 */
969 public void setHttpAuthUsernamePassword(String host, String realm,
970 String username, String password) {
971 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
972 }
973
974 /**
975 * Retrieve the HTTP authentication username and password for a given
976 * host & realm pair
977 *
978 * @param host The host for which the credentials apply.
979 * @param realm The realm for which the credentials apply.
980 * @return String[] if found, String[0] is username, which can be null and
981 * String[1] is password. Return null if it can't find anything.
982 */
983 public String[] getHttpAuthUsernamePassword(String host, String realm) {
984 return mDatabase.getHttpAuthUsernamePassword(host, realm);
985 }
986
987 /**
988 * Destroy the internal state of the WebView. This method should be called
989 * after the WebView has been removed from the view system. No other
990 * methods may be called on a WebView after destroy.
991 */
992 public void destroy() {
993 clearTextEntry();
994 if (mWebViewCore != null) {
995 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -0400996 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 mCallbackProxy.setWebViewClient(null);
998 mCallbackProxy.setWebChromeClient(null);
999 // Tell WebViewCore to destroy itself
1000 WebViewCore webViewCore = mWebViewCore;
1001 mWebViewCore = null; // prevent using partial webViewCore
1002 webViewCore.destroy();
1003 // Remove any pending messages that might not be serviced yet.
1004 mPrivateHandler.removeCallbacksAndMessages(null);
1005 mCallbackProxy.removeCallbacksAndMessages(null);
1006 // Wake up the WebCore thread just in case it is waiting for a
1007 // javascript dialog.
1008 synchronized (mCallbackProxy) {
1009 mCallbackProxy.notify();
1010 }
1011 }
1012 if (mNativeClass != 0) {
1013 nativeDestroy();
1014 mNativeClass = 0;
1015 }
1016 }
1017
1018 /**
1019 * Enables platform notifications of data state and proxy changes.
1020 */
1021 public static void enablePlatformNotifications() {
1022 Network.enablePlatformNotifications();
1023 }
1024
1025 /**
1026 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001027 * from the Activity's onPause() or onStop().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 */
1029 public static void disablePlatformNotifications() {
1030 Network.disablePlatformNotifications();
1031 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001032
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 /**
Feng Qianb3081372009-06-29 15:55:18 -07001034 * Sets JavaScript engine flags.
1035 *
1036 * @param flags JS engine flags in a String
1037 *
1038 * @hide pending API solidification
1039 */
1040 public void setJsFlags(String flags) {
1041 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1042 }
1043
1044 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001045 * Inform WebView of the network state. This is used to set
1046 * the javascript property window.navigator.isOnline and
1047 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1048 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001049 */
1050 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001051 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1052 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001053 }
1054
1055 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001056 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1058 * method no longer stores the display data for this WebView. The previous
1059 * behavior could potentially leak files if {@link #restoreState} was never
1060 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1061 * and restoring the display data.
1062 * @param outState The Bundle to store the WebView state.
1063 * @return The same copy of the back/forward list used to save the state. If
1064 * saveState fails, the returned list will be null.
1065 * @see #savePicture
1066 * @see #restorePicture
1067 */
1068 public WebBackForwardList saveState(Bundle outState) {
1069 if (outState == null) {
1070 return null;
1071 }
1072 // We grab a copy of the back/forward list because a client of WebView
1073 // may have invalidated the history list by calling clearHistory.
1074 WebBackForwardList list = copyBackForwardList();
1075 final int currentIndex = list.getCurrentIndex();
1076 final int size = list.getSize();
1077 // We should fail saving the state if the list is empty or the index is
1078 // not in a valid range.
1079 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1080 return null;
1081 }
1082 outState.putInt("index", currentIndex);
1083 // FIXME: This should just be a byte[][] instead of ArrayList but
1084 // Parcel.java does not have the code to handle multi-dimensional
1085 // arrays.
1086 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1087 for (int i = 0; i < size; i++) {
1088 WebHistoryItem item = list.getItemAtIndex(i);
1089 byte[] data = item.getFlattenedData();
1090 if (data == null) {
1091 // It would be very odd to not have any data for a given history
1092 // item. And we will fail to rebuild the history list without
1093 // flattened data.
1094 return null;
1095 }
1096 history.add(data);
1097 }
1098 outState.putSerializable("history", history);
1099 if (mCertificate != null) {
1100 outState.putBundle("certificate",
1101 SslCertificate.saveState(mCertificate));
1102 }
1103 return list;
1104 }
1105
1106 /**
1107 * Save the current display data to the Bundle given. Used in conjunction
1108 * with {@link #saveState}.
1109 * @param b A Bundle to store the display data.
1110 * @param dest The file to store the serialized picture data. Will be
1111 * overwritten with this WebView's picture data.
1112 * @return True if the picture was successfully saved.
1113 */
1114 public boolean savePicture(Bundle b, File dest) {
1115 if (dest == null || b == null) {
1116 return false;
1117 }
1118 final Picture p = capturePicture();
1119 try {
1120 final FileOutputStream out = new FileOutputStream(dest);
1121 p.writeToStream(out);
1122 out.close();
1123 } catch (FileNotFoundException e){
1124 e.printStackTrace();
1125 } catch (IOException e) {
1126 e.printStackTrace();
1127 } catch (RuntimeException e) {
1128 e.printStackTrace();
1129 }
1130 if (dest.length() > 0) {
1131 b.putInt("scrollX", mScrollX);
1132 b.putInt("scrollY", mScrollY);
1133 b.putFloat("scale", mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001134 if (mInZoomOverview) {
1135 b.putFloat("lastScale", mLastScale);
1136 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 return true;
1138 }
1139 return false;
1140 }
1141
1142 /**
1143 * Restore the display data that was save in {@link #savePicture}. Used in
1144 * conjunction with {@link #restoreState}.
1145 * @param b A Bundle containing the saved display data.
1146 * @param src The file where the picture data was stored.
1147 * @return True if the picture was successfully restored.
1148 */
1149 public boolean restorePicture(Bundle b, File src) {
1150 if (src == null || b == null) {
1151 return false;
1152 }
1153 if (src.exists()) {
1154 Picture p = null;
1155 try {
1156 final FileInputStream in = new FileInputStream(src);
1157 p = Picture.createFromStream(in);
1158 in.close();
1159 } catch (FileNotFoundException e){
1160 e.printStackTrace();
1161 } catch (RuntimeException e) {
1162 e.printStackTrace();
1163 } catch (IOException e) {
1164 e.printStackTrace();
1165 }
1166 if (p != null) {
1167 int sx = b.getInt("scrollX", 0);
1168 int sy = b.getInt("scrollY", 0);
1169 float scale = b.getFloat("scale", 1.0f);
1170 mDrawHistory = true;
1171 mHistoryPicture = p;
1172 mScrollX = sx;
1173 mScrollY = sy;
1174 mHistoryWidth = Math.round(p.getWidth() * scale);
1175 mHistoryHeight = Math.round(p.getHeight() * scale);
1176 // as getWidth() / getHeight() of the view are not
1177 // available yet, set up mActualScale, so that when
1178 // onSizeChanged() is called, the rest will be set
1179 // correctly
1180 mActualScale = scale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001181 float lastScale = b.getFloat("lastScale", -1.0f);
1182 if (lastScale > 0) {
1183 mInZoomOverview = true;
1184 mLastScale = lastScale;
1185 } else {
1186 mInZoomOverview = false;
1187 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 invalidate();
1189 return true;
1190 }
1191 }
1192 return false;
1193 }
1194
1195 /**
1196 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001197 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1198 * be called to restore the state of the WebView before using the object. If
1199 * it is called after the WebView has had a chance to build state (load
1200 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 * side-effects. Please note that this method no longer restores the
1202 * display data for this WebView. See {@link #savePicture} and {@link
1203 * #restorePicture} for saving and restoring the display data.
1204 * @param inState The incoming Bundle of state.
1205 * @return The restored back/forward list or null if restoreState failed.
1206 * @see #savePicture
1207 * @see #restorePicture
1208 */
1209 public WebBackForwardList restoreState(Bundle inState) {
1210 WebBackForwardList returnList = null;
1211 if (inState == null) {
1212 return returnList;
1213 }
1214 if (inState.containsKey("index") && inState.containsKey("history")) {
1215 mCertificate = SslCertificate.restoreState(
1216 inState.getBundle("certificate"));
1217
1218 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1219 final int index = inState.getInt("index");
1220 // We can't use a clone of the list because we need to modify the
1221 // shared copy, so synchronize instead to prevent concurrent
1222 // modifications.
1223 synchronized (list) {
1224 final List<byte[]> history =
1225 (List<byte[]>) inState.getSerializable("history");
1226 final int size = history.size();
1227 // Check the index bounds so we don't crash in native code while
1228 // restoring the history index.
1229 if (index < 0 || index >= size) {
1230 return null;
1231 }
1232 for (int i = 0; i < size; i++) {
1233 byte[] data = history.remove(0);
1234 if (data == null) {
1235 // If we somehow have null data, we cannot reconstruct
1236 // the item and thus our history list cannot be rebuilt.
1237 return null;
1238 }
1239 WebHistoryItem item = new WebHistoryItem(data);
1240 list.addHistoryItem(item);
1241 }
1242 // Grab the most recent copy to return to the caller.
1243 returnList = copyBackForwardList();
1244 // Update the copy to have the correct index.
1245 returnList.setCurrentIndex(index);
1246 }
1247 // Remove all pending messages because we are restoring previous
1248 // state.
1249 mWebViewCore.removeMessages();
1250 // Send a restore state message.
1251 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1252 }
1253 return returnList;
1254 }
1255
1256 /**
1257 * Load the given url.
1258 * @param url The url of the resource to load.
1259 */
1260 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001261 if (url == null) {
1262 return;
1263 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001264 switchOutDrawHistory();
1265 mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
1266 clearTextEntry();
1267 }
1268
1269 /**
Grace Kloba57534302009-05-22 18:55:02 -07001270 * Load the url with postData using "POST" method into the WebView. If url
1271 * is not a network url, it will be loaded with {link
1272 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001273 *
Grace Kloba57534302009-05-22 18:55:02 -07001274 * @param url The url of the resource to load.
1275 * @param postData The data will be passed to "POST" request.
Grace Kloba57534302009-05-22 18:55:02 -07001276 */
1277 public void postUrl(String url, byte[] postData) {
1278 if (URLUtil.isNetworkUrl(url)) {
1279 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001280 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1281 arg.mUrl = url;
1282 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001283 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
1284 clearTextEntry();
1285 } else {
1286 loadUrl(url);
1287 }
1288 }
1289
1290 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001291 * Load the given data into the WebView. This will load the data into
1292 * WebView using the data: scheme. Content loaded through this mechanism
1293 * does not have the ability to load content from the network.
1294 * @param data A String of data in the given encoding.
1295 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1296 * @param encoding The encoding of the data. i.e. utf-8, base64
1297 */
1298 public void loadData(String data, String mimeType, String encoding) {
1299 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1300 }
1301
1302 /**
1303 * Load the given data into the WebView, use the provided URL as the base
1304 * URL for the content. The base URL is the URL that represents the page
1305 * that is loaded through this interface. As such, it is used for the
1306 * history entry and to resolve any relative URLs. The failUrl is used if
1307 * browser fails to load the data provided. If it is empty or null, and the
1308 * load fails, then no history entry is created.
1309 * <p>
1310 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1311 * files through "file:///android_asset/" for the sub resources is more
1312 * restricted. If you provide null or empty string as baseUrl, you won't be
1313 * able to access asset files. If the baseUrl is anything other than
1314 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1315 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001316 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001317 * @param baseUrl Url to resolve relative paths with, if null defaults to
1318 * "about:blank"
1319 * @param data A String of data in the given encoding.
1320 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1321 * defaults to "text/html"
1322 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
1323 * @param failUrl URL to use if the content fails to load or null.
1324 */
1325 public void loadDataWithBaseURL(String baseUrl, String data,
1326 String mimeType, String encoding, String failUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001327
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001328 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1329 loadData(data, mimeType, encoding);
1330 return;
1331 }
1332 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001333 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1334 arg.mBaseUrl = baseUrl;
1335 arg.mData = data;
1336 arg.mMimeType = mimeType;
1337 arg.mEncoding = encoding;
1338 arg.mFailUrl = failUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
1340 clearTextEntry();
1341 }
1342
1343 /**
1344 * Stop the current load.
1345 */
1346 public void stopLoading() {
1347 // TODO: should we clear all the messages in the queue before sending
1348 // STOP_LOADING?
1349 switchOutDrawHistory();
1350 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1351 }
1352
1353 /**
1354 * Reload the current url.
1355 */
1356 public void reload() {
1357 switchOutDrawHistory();
1358 mWebViewCore.sendMessage(EventHub.RELOAD);
1359 }
1360
1361 /**
1362 * Return true if this WebView has a back history item.
1363 * @return True iff this WebView has a back history item.
1364 */
1365 public boolean canGoBack() {
1366 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1367 synchronized (l) {
1368 if (l.getClearPending()) {
1369 return false;
1370 } else {
1371 return l.getCurrentIndex() > 0;
1372 }
1373 }
1374 }
1375
1376 /**
1377 * Go back in the history of this WebView.
1378 */
1379 public void goBack() {
1380 goBackOrForward(-1);
1381 }
1382
1383 /**
1384 * Return true if this WebView has a forward history item.
1385 * @return True iff this Webview has a forward history item.
1386 */
1387 public boolean canGoForward() {
1388 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1389 synchronized (l) {
1390 if (l.getClearPending()) {
1391 return false;
1392 } else {
1393 return l.getCurrentIndex() < l.getSize() - 1;
1394 }
1395 }
1396 }
1397
1398 /**
1399 * Go forward in the history of this WebView.
1400 */
1401 public void goForward() {
1402 goBackOrForward(1);
1403 }
1404
1405 /**
1406 * Return true if the page can go back or forward the given
1407 * number of steps.
1408 * @param steps The negative or positive number of steps to move the
1409 * history.
1410 */
1411 public boolean canGoBackOrForward(int steps) {
1412 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1413 synchronized (l) {
1414 if (l.getClearPending()) {
1415 return false;
1416 } else {
1417 int newIndex = l.getCurrentIndex() + steps;
1418 return newIndex >= 0 && newIndex < l.getSize();
1419 }
1420 }
1421 }
1422
1423 /**
1424 * Go to the history item that is the number of steps away from
1425 * the current item. Steps is negative if backward and positive
1426 * if forward.
1427 * @param steps The number of steps to take back or forward in the back
1428 * forward list.
1429 */
1430 public void goBackOrForward(int steps) {
1431 goBackOrForward(steps, false);
1432 }
1433
1434 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
1435 // every time we go back or forward, we want to reset the
1436 // WebView certificate:
1437 // if the new site is secure, we will reload it and get a
1438 // new certificate set;
1439 // if the new site is not secure, the certificate must be
1440 // null, and that will be the case
1441 mCertificate = null;
1442 if (steps != 0) {
1443 clearTextEntry();
1444 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
1445 ignoreSnapshot ? 1 : 0);
1446 }
1447 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001448
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 private boolean extendScroll(int y) {
1450 int finalY = mScroller.getFinalY();
1451 int newY = pinLocY(finalY + y);
1452 if (newY == finalY) return false;
1453 mScroller.setFinalY(newY);
1454 mScroller.extendDuration(computeDuration(0, y));
1455 return true;
1456 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001457
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001458 /**
1459 * Scroll the contents of the view up by half the view size
1460 * @param top true to jump to the top of the page
1461 * @return true if the page was scrolled
1462 */
1463 public boolean pageUp(boolean top) {
1464 if (mNativeClass == 0) {
1465 return false;
1466 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001467 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001468 if (top) {
1469 // go to the top of the document
1470 return pinScrollTo(mScrollX, 0, true, 0);
1471 }
1472 // Page up
1473 int h = getHeight();
1474 int y;
1475 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1476 y = -h + PAGE_SCROLL_OVERLAP;
1477 } else {
1478 y = -h / 2;
1479 }
1480 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001481 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001482 : extendScroll(y);
1483 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 /**
1486 * Scroll the contents of the view down by half the page size
1487 * @param bottom true to jump to bottom of page
1488 * @return true if the page was scrolled
1489 */
1490 public boolean pageDown(boolean bottom) {
1491 if (mNativeClass == 0) {
1492 return false;
1493 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001494 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001495 if (bottom) {
1496 return pinScrollTo(mScrollX, mContentHeight, true, 0);
1497 }
1498 // Page down.
1499 int h = getHeight();
1500 int y;
1501 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1502 y = h - PAGE_SCROLL_OVERLAP;
1503 } else {
1504 y = h / 2;
1505 }
1506 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001507 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001508 : extendScroll(y);
1509 }
1510
1511 /**
1512 * Clear the view so that onDraw() will draw nothing but white background,
1513 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
1514 */
1515 public void clearView() {
1516 mContentWidth = 0;
1517 mContentHeight = 0;
1518 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
1519 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001520
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001521 /**
1522 * Return a new picture that captures the current display of the webview.
1523 * This is a copy of the display, and will be unaffected if the webview
1524 * later loads a different URL.
1525 *
1526 * @return a picture containing the current contents of the view. Note this
1527 * picture is of the entire document, and is not restricted to the
1528 * bounds of the view.
1529 */
1530 public Picture capturePicture() {
Cary Clarkd6982c92009-05-29 11:02:22 -04001531 if (null == mWebViewCore) return null; // check for out of memory tab
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001532 return mWebViewCore.copyContentPicture();
1533 }
1534
1535 /**
1536 * Return true if the browser is displaying a TextView for text input.
1537 */
1538 private boolean inEditingMode() {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001539 return mWebTextView != null && mWebTextView.getParent() != null
1540 && mWebTextView.hasFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001541 }
1542
1543 private void clearTextEntry() {
1544 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001545 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001546 }
1547 }
1548
Cary Clarkd6982c92009-05-29 11:02:22 -04001549 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 * Return the current scale of the WebView
1551 * @return The current scale.
1552 */
1553 public float getScale() {
1554 return mActualScale;
1555 }
1556
1557 /**
1558 * Set the initial scale for the WebView. 0 means default. If
1559 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
1560 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
1561 * WebView starts will this value as initial scale.
1562 *
1563 * @param scaleInPercent The initial scale in percent.
1564 */
1565 public void setInitialScale(int scaleInPercent) {
1566 mInitialScale = scaleInPercent;
1567 }
1568
1569 /**
1570 * Invoke the graphical zoom picker widget for this WebView. This will
1571 * result in the zoom widget appearing on the screen to control the zoom
1572 * level of this WebView.
1573 */
1574 public void invokeZoomPicker() {
1575 if (!getSettings().supportZoom()) {
1576 Log.w(LOGTAG, "This WebView doesn't support zoom.");
1577 return;
1578 }
1579 clearTextEntry();
The Android Open Source Project10592532009-03-18 17:39:46 -07001580 if (getSettings().getBuiltInZoomControls()) {
1581 mZoomButtonsController.setVisible(true);
1582 } else {
1583 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
1584 mPrivateHandler.postDelayed(mZoomControlRunnable,
1585 ZOOM_CONTROLS_TIMEOUT);
1586 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001587 }
1588
1589 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001590 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001591 * is found and the anchor has a non-javascript url, the HitTestResult type
1592 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
1593 * anchor does not have a url or if it is a javascript url, the type will
1594 * be UNKNOWN_TYPE and the url has to be retrieved through
1595 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
1596 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
1597 * the "extra" field. A type of
1598 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
1599 * a child node. If a phone number is found, the HitTestResult type is set
1600 * to PHONE_TYPE and the phone number is set in the "extra" field of
1601 * HitTestResult. If a map address is found, the HitTestResult type is set
1602 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
1603 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
1604 * and the email is set in the "extra" field of HitTestResult. Otherwise,
1605 * HitTestResult type is set to UNKNOWN_TYPE.
1606 */
1607 public HitTestResult getHitTestResult() {
1608 if (mNativeClass == 0) {
1609 return null;
1610 }
1611
1612 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04001613 if (nativeHasCursorNode()) {
1614 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001615 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04001616 } else {
1617 String text = nativeCursorText();
1618 if (text != null) {
1619 if (text.startsWith(SCHEME_TEL)) {
1620 result.setType(HitTestResult.PHONE_TYPE);
1621 result.setExtra(text.substring(SCHEME_TEL.length()));
1622 } else if (text.startsWith(SCHEME_MAILTO)) {
1623 result.setType(HitTestResult.EMAIL_TYPE);
1624 result.setExtra(text.substring(SCHEME_MAILTO.length()));
1625 } else if (text.startsWith(SCHEME_GEO)) {
1626 result.setType(HitTestResult.GEO_TYPE);
1627 result.setExtra(URLDecoder.decode(text
1628 .substring(SCHEME_GEO.length())));
1629 } else if (nativeCursorIsAnchor()) {
1630 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
1631 result.setExtra(text);
1632 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633 }
1634 }
1635 }
1636 int type = result.getType();
1637 if (type == HitTestResult.UNKNOWN_TYPE
1638 || type == HitTestResult.SRC_ANCHOR_TYPE) {
1639 // Now check to see if it is an image.
1640 int contentX = viewToContent((int) mLastTouchX + mScrollX);
1641 int contentY = viewToContent((int) mLastTouchY + mScrollY);
1642 String text = nativeImageURI(contentX, contentY);
1643 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001644 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
1645 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001646 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
1647 result.setExtra(text);
1648 }
1649 }
1650 return result;
1651 }
1652
1653 /**
1654 * Request the href of an anchor element due to getFocusNodePath returning
1655 * "href." If hrefMsg is null, this method returns immediately and does not
1656 * dispatch hrefMsg to its target.
Cary Clarkd6982c92009-05-29 11:02:22 -04001657 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001658 * @param hrefMsg This message will be dispatched with the result of the
1659 * request as the data member with "url" as key. The result can
1660 * be null.
1661 */
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001662 // FIXME: API change required to change the name of this function. We now
1663 // look at the cursor node, and not the focus node. Also, what is
1664 // getFocusNodePath?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001665 public void requestFocusNodeHref(Message hrefMsg) {
1666 if (hrefMsg == null || mNativeClass == 0) {
1667 return;
1668 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001669 if (nativeCursorIsAnchor()) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001670 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
Cary Clarkd6982c92009-05-29 11:02:22 -04001671 nativeCursorFramePointer(), nativeCursorNodePointer(),
1672 hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 }
1674 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001675
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001676 /**
1677 * Request the url of the image last touched by the user. msg will be sent
1678 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04001679 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001680 * @param msg This message will be dispatched with the result of the request
1681 * as the data member with "url" as key. The result can be null.
1682 */
1683 public void requestImageRef(Message msg) {
1684 int contentX = viewToContent((int) mLastTouchX + mScrollX);
1685 int contentY = viewToContent((int) mLastTouchY + mScrollY);
1686 String ref = nativeImageURI(contentX, contentY);
1687 Bundle data = msg.getData();
1688 data.putString("url", ref);
1689 msg.setData(data);
1690 msg.sendToTarget();
1691 }
1692
1693 private static int pinLoc(int x, int viewMax, int docMax) {
1694// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
1695 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001696 // pin the short document to the top/left of the screen
1697 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001698// Log.d(LOGTAG, "--- center " + x);
1699 } else if (x < 0) {
1700 x = 0;
1701// Log.d(LOGTAG, "--- zero");
1702 } else if (x + viewMax > docMax) {
1703 x = docMax - viewMax;
1704// Log.d(LOGTAG, "--- pin " + x);
1705 }
1706 return x;
1707 }
1708
1709 // Expects x in view coordinates
1710 private int pinLocX(int x) {
1711 return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
1712 }
1713
1714 // Expects y in view coordinates
1715 private int pinLocY(int y) {
1716 return pinLoc(y, getViewHeight(), computeVerticalScrollRange());
1717 }
1718
1719 /*package*/ int viewToContent(int x) {
1720 return Math.round(x * mInvActualScale);
1721 }
1722
Patrick Scott0a5ce012009-07-02 08:56:10 -04001723 /*package*/ int contentToView(int x) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724 return Math.round(x * mActualScale);
1725 }
1726
1727 // Called by JNI to invalidate the View, given rectangle coordinates in
1728 // content space
1729 private void viewInvalidate(int l, int t, int r, int b) {
1730 invalidate(contentToView(l), contentToView(t), contentToView(r),
1731 contentToView(b));
1732 }
1733
1734 // Called by JNI to invalidate the View after a delay, given rectangle
1735 // coordinates in content space
1736 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
1737 postInvalidateDelayed(delay, contentToView(l), contentToView(t),
1738 contentToView(r), contentToView(b));
1739 }
1740
1741 private Rect contentToView(Rect x) {
1742 return new Rect(contentToView(x.left), contentToView(x.top)
1743 , contentToView(x.right), contentToView(x.bottom));
1744 }
1745
1746 /* call from webcoreview.draw(), so we're still executing in the UI thread
1747 */
1748 private void recordNewContentSize(int w, int h, boolean updateLayout) {
1749
1750 // premature data from webkit, ignore
1751 if ((w | h) == 0) {
1752 return;
1753 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001754
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001755 // don't abort a scroll animation if we didn't change anything
1756 if (mContentWidth != w || mContentHeight != h) {
1757 // record new dimensions
1758 mContentWidth = w;
1759 mContentHeight = h;
1760 // If history Picture is drawn, don't update scroll. They will be
1761 // updated when we get out of that mode.
1762 if (!mDrawHistory) {
1763 // repin our scroll, taking into account the new content size
1764 int oldX = mScrollX;
1765 int oldY = mScrollY;
1766 mScrollX = pinLocX(mScrollX);
1767 mScrollY = pinLocY(mScrollY);
1768 // android.util.Log.d("skia", "recordNewContentSize -
1769 // abortAnimation");
1770 mScroller.abortAnimation(); // just in case
1771 if (oldX != mScrollX || oldY != mScrollY) {
1772 sendOurVisibleRect();
1773 }
1774 }
1775 }
1776 contentSizeChanged(updateLayout);
1777 }
1778
1779 private void setNewZoomScale(float scale, boolean force) {
1780 if (scale < mMinZoomScale) {
1781 scale = mMinZoomScale;
1782 } else if (scale > mMaxZoomScale) {
1783 scale = mMaxZoomScale;
1784 }
1785 if (scale != mActualScale || force) {
1786 if (mDrawHistory) {
1787 // If history Picture is drawn, don't update scroll. They will
1788 // be updated when we get out of that mode.
1789 if (scale != mActualScale && !mPreviewZoomOnly) {
1790 mCallbackProxy.onScaleChanged(mActualScale, scale);
1791 }
1792 mActualScale = scale;
1793 mInvActualScale = 1 / scale;
1794 if (!mPreviewZoomOnly) {
1795 sendViewSizeZoom();
1796 }
1797 } else {
1798 // update our scroll so we don't appear to jump
1799 // i.e. keep the center of the doc in the center of the view
1800
1801 int oldX = mScrollX;
1802 int oldY = mScrollY;
1803 float ratio = scale * mInvActualScale; // old inverse
1804 float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
1805 float sy = ratio * oldY + (ratio - 1) * mZoomCenterY;
1806
1807 // now update our new scale and inverse
1808 if (scale != mActualScale && !mPreviewZoomOnly) {
1809 mCallbackProxy.onScaleChanged(mActualScale, scale);
1810 }
1811 mActualScale = scale;
1812 mInvActualScale = 1 / scale;
1813
Patrick Scott0a5ce012009-07-02 08:56:10 -04001814 // Scale all the child views
1815 mViewManager.scaleAll();
1816
Cary Clarkd6982c92009-05-29 11:02:22 -04001817 // as we don't have animation for scaling, don't do animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001818 // for scrolling, as it causes weird intermediate state
1819 // pinScrollTo(Math.round(sx), Math.round(sy));
1820 mScrollX = pinLocX(Math.round(sx));
1821 mScrollY = pinLocY(Math.round(sy));
1822
1823 if (!mPreviewZoomOnly) {
1824 sendViewSizeZoom();
1825 sendOurVisibleRect();
1826 }
1827 }
1828 }
1829 }
1830
1831 // Used to avoid sending many visible rect messages.
1832 private Rect mLastVisibleRectSent;
1833 private Rect mLastGlobalRect;
1834
1835 private Rect sendOurVisibleRect() {
1836 Rect rect = new Rect();
1837 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001838 // Rect.equals() checks for null input.
1839 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04001840 Point pos = new Point(rect.left, rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001841 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Cary Clarked56eda2009-06-18 09:48:47 -04001842 nativeMoveGeneration(), 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 mLastVisibleRectSent = rect;
1844 }
1845 Rect globalRect = new Rect();
1846 if (getGlobalVisibleRect(globalRect)
1847 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04001848 if (DebugFlags.WEB_VIEW) {
1849 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
1850 + globalRect.top + ",r=" + globalRect.right + ",b="
1851 + globalRect.bottom);
1852 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001853 // TODO: the global offset is only used by windowRect()
1854 // in ChromeClientAndroid ; other clients such as touch
1855 // and mouse events could return view + screen relative points.
1856 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
1857 mLastGlobalRect = globalRect;
1858 }
1859 return rect;
1860 }
1861
1862 // Sets r to be the visible rectangle of our webview in view coordinates
1863 private void calcOurVisibleRect(Rect r) {
1864 Point p = new Point();
1865 getGlobalVisibleRect(r, p);
1866 r.offset(-p.x, -p.y);
Cary Clark3524be92009-06-22 13:09:11 -04001867 if (mFindIsUp) {
1868 r.bottom -= FIND_HEIGHT;
1869 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 }
1871
1872 // Sets r to be our visible rectangle in content coordinates
1873 private void calcOurContentVisibleRect(Rect r) {
1874 calcOurVisibleRect(r);
1875 r.left = viewToContent(r.left);
1876 r.top = viewToContent(r.top);
1877 r.right = viewToContent(r.right);
1878 r.bottom = viewToContent(r.bottom);
1879 }
1880
Grace Klobaef347ef2009-07-30 11:20:32 -07001881 static class ViewSizeData {
1882 int mWidth;
1883 int mHeight;
1884 int mTextWrapWidth;
1885 float mScale;
1886 }
1887
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001888 /**
1889 * Compute unzoomed width and height, and if they differ from the last
1890 * values we sent, send them to webkit (to be used has new viewport)
1891 *
1892 * @return true if new values were sent
1893 */
1894 private boolean sendViewSizeZoom() {
Grace Klobaef347ef2009-07-30 11:20:32 -07001895 int viewWidth = getViewWidth();
1896 int newWidth = Math.round(viewWidth * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001897 int newHeight = Math.round(getViewHeight() * mInvActualScale);
1898 /*
1899 * Because the native side may have already done a layout before the
1900 * View system was able to measure us, we have to send a height of 0 to
1901 * remove excess whitespace when we grow our width. This will trigger a
1902 * layout and a change in content size. This content size change will
1903 * mean that contentSizeChanged will either call this method directly or
1904 * indirectly from onSizeChanged.
1905 */
1906 if (newWidth > mLastWidthSent && mWrapContent) {
1907 newHeight = 0;
1908 }
1909 // Avoid sending another message if the dimensions have not changed.
1910 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
Grace Klobaef347ef2009-07-30 11:20:32 -07001911 ViewSizeData data = new ViewSizeData();
1912 data.mWidth = newWidth;
1913 data.mHeight = newHeight;
1914 // while in zoom overview mode, the text are wrapped to the screen
1915 // width matching mLastScale. So that we don't trigger re-flow while
1916 // toggling between overview mode and normal mode.
1917 data.mTextWrapWidth = mInZoomOverview ? Math.round(viewWidth
1918 / mLastScale) : newWidth;
1919 data.mScale = mActualScale;
1920 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001921 mLastWidthSent = newWidth;
1922 mLastHeightSent = newHeight;
1923 return true;
1924 }
1925 return false;
1926 }
1927
1928 @Override
1929 protected int computeHorizontalScrollRange() {
1930 if (mDrawHistory) {
1931 return mHistoryWidth;
1932 } else {
1933 return contentToView(mContentWidth);
1934 }
1935 }
1936
1937 // Make sure this stays in sync with the actual height of the FindDialog.
1938 private static final int FIND_HEIGHT = 79;
1939
1940 @Override
1941 protected int computeVerticalScrollRange() {
1942 if (mDrawHistory) {
1943 return mHistoryHeight;
1944 } else {
1945 int height = contentToView(mContentHeight);
1946 if (mFindIsUp) {
1947 height += FIND_HEIGHT;
1948 }
1949 return height;
1950 }
1951 }
1952
1953 /**
1954 * Get the url for the current page. This is not always the same as the url
1955 * passed to WebViewClient.onPageStarted because although the load for
1956 * that url has begun, the current page may not have changed.
1957 * @return The url for the current page.
1958 */
1959 public String getUrl() {
1960 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1961 return h != null ? h.getUrl() : null;
1962 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001963
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001964 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001965 * Get the original url for the current page. This is not always the same
1966 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001967 * load for that url has begun, the current page may not have changed.
1968 * Also, there may have been redirects resulting in a different url to that
1969 * originally requested.
1970 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001971 */
1972 public String getOriginalUrl() {
1973 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1974 return h != null ? h.getOriginalUrl() : null;
1975 }
1976
1977 /**
1978 * Get the title for the current page. This is the title of the current page
1979 * until WebViewClient.onReceivedTitle is called.
1980 * @return The title for the current page.
1981 */
1982 public String getTitle() {
1983 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1984 return h != null ? h.getTitle() : null;
1985 }
1986
1987 /**
1988 * Get the favicon for the current page. This is the favicon of the current
1989 * page until WebViewClient.onReceivedIcon is called.
1990 * @return The favicon for the current page.
1991 */
1992 public Bitmap getFavicon() {
1993 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1994 return h != null ? h.getFavicon() : null;
1995 }
1996
1997 /**
Patrick Scott2ba12622009-08-04 13:20:05 -04001998 * Get the touch icon url for the apple-touch-icon <link> element.
1999 * @hide
2000 */
2001 public String getTouchIconUrl() {
2002 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2003 return h != null ? h.getTouchIconUrl() : null;
2004 }
2005
2006 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002007 * Get the progress for the current page.
2008 * @return The progress for the current page between 0 and 100.
2009 */
2010 public int getProgress() {
2011 return mCallbackProxy.getProgress();
2012 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002013
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002014 /**
2015 * @return the height of the HTML content.
2016 */
2017 public int getContentHeight() {
2018 return mContentHeight;
2019 }
2020
2021 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002022 * Pause all layout, parsing, and javascript timers for all webviews. This
2023 * is a global requests, not restricted to just this webview. This can be
2024 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002025 */
2026 public void pauseTimers() {
2027 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
2028 }
2029
2030 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002031 * Resume all layout, parsing, and javascript timers for all webviews.
2032 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 */
2034 public void resumeTimers() {
2035 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
2036 }
2037
2038 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002039 * Call this to pause any extra processing associated with this view and
2040 * its associated DOM/plugins/javascript/etc. For example, if the view is
2041 * taken offscreen, this could be called to reduce unnecessary CPU and/or
2042 * network traffic. When the view is again "active", call onResume().
2043 *
2044 * Note that this differs from pauseTimers(), which affects all views/DOMs
2045 * @hide
2046 */
2047 public void onPause() {
2048 if (!mIsPaused) {
2049 mIsPaused = true;
2050 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
2051 }
2052 }
2053
2054 /**
2055 * Call this to balanace a previous call to onPause()
2056 * @hide
2057 */
2058 public void onResume() {
2059 if (mIsPaused) {
2060 mIsPaused = false;
2061 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2062 }
2063 }
2064
2065 /**
2066 * Returns true if the view is paused, meaning onPause() was called. Calling
2067 * onResume() sets the paused state back to false.
2068 * @hide
2069 */
2070 public boolean isPaused() {
2071 return mIsPaused;
2072 }
2073
2074 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002075 * Call this to inform the view that memory is low so that it can
2076 * free any available memory.
2077 * @hide
2078 */
2079 public void freeMemory() {
2080 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2081 }
2082
2083 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002084 * Clear the resource cache. Note that the cache is per-application, so
2085 * this will clear the cache for all WebViews used.
2086 *
2087 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002088 */
2089 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002090 // Note: this really needs to be a static method as it clears cache for all
2091 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2092 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2094 includeDiskFiles ? 1 : 0, 0);
2095 }
2096
2097 /**
2098 * Make sure that clearing the form data removes the adapter from the
2099 * currently focused textfield if there is one.
2100 */
2101 public void clearFormData() {
2102 if (inEditingMode()) {
2103 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002104 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002105 }
2106 }
2107
2108 /**
2109 * Tell the WebView to clear its internal back/forward list.
2110 */
2111 public void clearHistory() {
2112 mCallbackProxy.getBackForwardList().setClearPending();
2113 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2114 }
2115
2116 /**
2117 * Clear the SSL preferences table stored in response to proceeding with SSL
2118 * certificate errors.
2119 */
2120 public void clearSslPreferences() {
2121 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2122 }
2123
2124 /**
2125 * Return the WebBackForwardList for this WebView. This contains the
2126 * back/forward list for use in querying each item in the history stack.
2127 * This is a copy of the private WebBackForwardList so it contains only a
2128 * snapshot of the current state. Multiple calls to this method may return
2129 * different objects. The object returned from this method will not be
2130 * updated to reflect any new state.
2131 */
2132 public WebBackForwardList copyBackForwardList() {
2133 return mCallbackProxy.getBackForwardList().clone();
2134 }
2135
2136 /*
2137 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04002138 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002139 * calling findAll.
2140 *
2141 * @param forward Direction to search.
2142 */
2143 public void findNext(boolean forward) {
2144 nativeFindNext(forward);
2145 }
2146
2147 /*
2148 * Find all instances of find on the page and highlight them.
2149 * @param find String to find.
2150 * @return int The number of occurances of the String "find"
2151 * that were found.
2152 */
2153 public int findAll(String find) {
2154 mFindIsUp = true;
2155 int result = nativeFindAll(find.toLowerCase(), find.toUpperCase());
2156 invalidate();
2157 return result;
2158 }
2159
2160 // Used to know whether the find dialog is open. Affects whether
2161 // or not we draw the highlights for matches.
2162 private boolean mFindIsUp;
2163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002164 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002165 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002166 * location. Currently, only addresses in the United States are detected,
2167 * and consist of:
2168 * - a house number
2169 * - a street name
2170 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2171 * - a city name
2172 * - a state or territory, either spelled out or two-letter abbr.
2173 * - an optional 5 digit or 9 digit zip code.
2174 *
2175 * All names must be correctly capitalized, and the zip code, if present,
2176 * must be valid for the state. The street type must be a standard USPS
2177 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04002178 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002179 * five digits.
2180 * @param addr The string to search for addresses.
2181 *
2182 * @return the address, or if no address is found, return null.
2183 */
2184 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04002185 return findAddress(addr, false);
2186 }
2187
2188 /**
2189 * @hide
2190 * Return the first substring consisting of the address of a physical
2191 * location. Currently, only addresses in the United States are detected,
2192 * and consist of:
2193 * - a house number
2194 * - a street name
2195 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2196 * - a city name
2197 * - a state or territory, either spelled out or two-letter abbr.
2198 * - an optional 5 digit or 9 digit zip code.
2199 *
2200 * Names are optionally capitalized, and the zip code, if present,
2201 * must be valid for the state. The street type must be a standard USPS
2202 * spelling or abbreviation. The state or territory must also be spelled
2203 * or abbreviated using USPS standards. The house number may not exceed
2204 * five digits.
2205 * @param addr The string to search for addresses.
2206 * @param caseInsensitive addr Set to true to make search ignore case.
2207 *
2208 * @return the address, or if no address is found, return null.
2209 */
2210 public static String findAddress(String addr, boolean caseInsensitive) {
2211 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002212 }
2213
2214 /*
2215 * Clear the highlighting surrounding text matches created by findAll.
2216 */
2217 public void clearMatches() {
2218 mFindIsUp = false;
2219 nativeSetFindIsDown();
2220 // Now that the dialog has been removed, ensure that we scroll to a
2221 // location that is not beyond the end of the page.
2222 pinScrollTo(mScrollX, mScrollY, false, 0);
2223 invalidate();
2224 }
2225
2226 /**
2227 * Query the document to see if it contains any image references. The
2228 * message object will be dispatched with arg1 being set to 1 if images
2229 * were found and 0 if the document does not reference any images.
2230 * @param response The message that will be dispatched with the result.
2231 */
2232 public void documentHasImages(Message response) {
2233 if (response == null) {
2234 return;
2235 }
2236 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
2237 }
2238
2239 @Override
2240 public void computeScroll() {
2241 if (mScroller.computeScrollOffset()) {
2242 int oldX = mScrollX;
2243 int oldY = mScrollY;
2244 mScrollX = mScroller.getCurrX();
2245 mScrollY = mScroller.getCurrY();
2246 postInvalidate(); // So we draw again
2247 if (oldX != mScrollX || oldY != mScrollY) {
2248 // as onScrollChanged() is not called, sendOurVisibleRect()
2249 // needs to be call explicitly
2250 sendOurVisibleRect();
2251 }
2252 } else {
2253 super.computeScroll();
2254 }
2255 }
2256
2257 private static int computeDuration(int dx, int dy) {
2258 int distance = Math.max(Math.abs(dx), Math.abs(dy));
2259 int duration = distance * 1000 / STD_SPEED;
2260 return Math.min(duration, MAX_DURATION);
2261 }
2262
2263 // helper to pin the scrollBy parameters (already in view coordinates)
2264 // returns true if the scroll was changed
2265 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
2266 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
2267 }
2268
2269 // helper to pin the scrollTo parameters (already in view coordinates)
2270 // returns true if the scroll was changed
2271 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
2272 x = pinLocX(x);
2273 y = pinLocY(y);
2274 int dx = x - mScrollX;
2275 int dy = y - mScrollY;
2276
2277 if ((dx | dy) == 0) {
2278 return false;
2279 }
2280
2281 if (true && animate) {
2282 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
2283
2284 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
2285 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
2286 invalidate();
2287 } else {
2288 mScroller.abortAnimation(); // just in case
2289 scrollTo(x, y);
2290 }
2291 return true;
2292 }
2293
2294 // Scale from content to view coordinates, and pin.
2295 // Also called by jni webview.cpp
Leon Scroggins4c943042009-07-31 15:05:42 -04002296 private boolean setContentScrollBy(int cx, int cy, boolean animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002297 if (mDrawHistory) {
2298 // disallow WebView to change the scroll position as History Picture
2299 // is used in the view system.
2300 // TODO: as we switchOutDrawHistory when trackball or navigation
2301 // keys are hit, this should be safe. Right?
Leon Scroggins4c943042009-07-31 15:05:42 -04002302 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002303 }
2304 cx = contentToView(cx);
2305 cy = contentToView(cy);
2306 if (mHeightCanMeasure) {
2307 // move our visible rect according to scroll request
2308 if (cy != 0) {
2309 Rect tempRect = new Rect();
2310 calcOurVisibleRect(tempRect);
2311 tempRect.offset(cx, cy);
2312 requestRectangleOnScreen(tempRect);
2313 }
2314 // FIXME: We scroll horizontally no matter what because currently
2315 // ScrollView and ListView will not scroll horizontally.
2316 // FIXME: Why do we only scroll horizontally if there is no
2317 // vertical scroll?
2318// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
Leon Scroggins4c943042009-07-31 15:05:42 -04002319 return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002320 } else {
Leon Scroggins4c943042009-07-31 15:05:42 -04002321 return pinScrollBy(cx, cy, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002322 }
2323 }
2324
2325 // scale from content to view coordinates, and pin
2326 // return true if pin caused the final x/y different than the request cx/cy;
2327 // return false if the view scroll to the exact position as it is requested.
2328 private boolean setContentScrollTo(int cx, int cy) {
2329 if (mDrawHistory) {
2330 // disallow WebView to change the scroll position as History Picture
2331 // is used in the view system.
2332 // One known case where this is called is that WebCore tries to
2333 // restore the scroll position. As history Picture already uses the
2334 // saved scroll position, it is ok to skip this.
2335 return false;
2336 }
2337 int vx = contentToView(cx);
2338 int vy = contentToView(cy);
2339// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
2340// vx + " " + vy + "]");
2341 pinScrollTo(vx, vy, false, 0);
2342 if (mScrollX != vx || mScrollY != vy) {
2343 return true;
2344 } else {
2345 return false;
2346 }
2347 }
2348
2349 // scale from content to view coordinates, and pin
2350 private void spawnContentScrollTo(int cx, int cy) {
2351 if (mDrawHistory) {
2352 // disallow WebView to change the scroll position as History Picture
2353 // is used in the view system.
2354 return;
2355 }
2356 int vx = contentToView(cx);
2357 int vy = contentToView(cy);
2358 pinScrollTo(vx, vy, true, 0);
2359 }
2360
2361 /**
2362 * These are from webkit, and are in content coordinate system (unzoomed)
2363 */
2364 private void contentSizeChanged(boolean updateLayout) {
2365 // suppress 0,0 since we usually see real dimensions soon after
2366 // this avoids drawing the prev content in a funny place. If we find a
2367 // way to consolidate these notifications, this check may become
2368 // obsolete
2369 if ((mContentWidth | mContentHeight) == 0) {
2370 return;
2371 }
2372
2373 if (mHeightCanMeasure) {
2374 if (getMeasuredHeight() != contentToView(mContentHeight)
2375 && updateLayout) {
2376 requestLayout();
2377 }
2378 } else if (mWidthCanMeasure) {
2379 if (getMeasuredWidth() != contentToView(mContentWidth)
2380 && updateLayout) {
2381 requestLayout();
2382 }
2383 } else {
2384 // If we don't request a layout, try to send our view size to the
2385 // native side to ensure that WebCore has the correct dimensions.
2386 sendViewSizeZoom();
2387 }
2388 }
2389
2390 /**
2391 * Set the WebViewClient that will receive various notifications and
2392 * requests. This will replace the current handler.
2393 * @param client An implementation of WebViewClient.
2394 */
2395 public void setWebViewClient(WebViewClient client) {
2396 mCallbackProxy.setWebViewClient(client);
2397 }
2398
2399 /**
2400 * Register the interface to be used when content can not be handled by
2401 * the rendering engine, and should be downloaded instead. This will replace
2402 * the current handler.
2403 * @param listener An implementation of DownloadListener.
2404 */
2405 public void setDownloadListener(DownloadListener listener) {
2406 mCallbackProxy.setDownloadListener(listener);
2407 }
2408
2409 /**
2410 * Set the chrome handler. This is an implementation of WebChromeClient for
2411 * use in handling Javascript dialogs, favicons, titles, and the progress.
2412 * This will replace the current handler.
2413 * @param client An implementation of WebChromeClient.
2414 */
2415 public void setWebChromeClient(WebChromeClient client) {
2416 mCallbackProxy.setWebChromeClient(client);
2417 }
2418
2419 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01002420 * Gets the chrome handler.
2421 * @return the current WebChromeClient instance.
2422 *
2423 * @hide API council approval.
2424 */
2425 public WebChromeClient getWebChromeClient() {
2426 return mCallbackProxy.getWebChromeClient();
2427 }
2428
2429 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002430 * Set the Picture listener. This is an interface used to receive
2431 * notifications of a new Picture.
2432 * @param listener An implementation of WebView.PictureListener.
2433 */
2434 public void setPictureListener(PictureListener listener) {
2435 mPictureListener = listener;
2436 }
2437
2438 /**
2439 * {@hide}
2440 */
2441 /* FIXME: Debug only! Remove for SDK! */
2442 public void externalRepresentation(Message callback) {
2443 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
2444 }
2445
2446 /**
2447 * {@hide}
2448 */
2449 /* FIXME: Debug only! Remove for SDK! */
2450 public void documentAsText(Message callback) {
2451 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
2452 }
2453
2454 /**
2455 * Use this function to bind an object to Javascript so that the
2456 * methods can be accessed from Javascript.
2457 * <p><strong>IMPORTANT:</strong>
2458 * <ul>
2459 * <li> Using addJavascriptInterface() allows JavaScript to control your
2460 * application. This can be a very useful feature or a dangerous security
2461 * issue. When the HTML in the WebView is untrustworthy (for example, part
2462 * or all of the HTML is provided by some person or process), then an
2463 * attacker could inject HTML that will execute your code and possibly any
2464 * code of the attacker's choosing.<br>
2465 * Do not use addJavascriptInterface() unless all of the HTML in this
2466 * WebView was written by you.</li>
2467 * <li> The Java object that is bound runs in another thread and not in
2468 * the thread that it was constructed in.</li>
2469 * </ul></p>
2470 * @param obj The class instance to bind to Javascript
2471 * @param interfaceName The name to used to expose the class in Javascript
2472 */
2473 public void addJavascriptInterface(Object obj, String interfaceName) {
Cary Clarkded054c2009-06-15 10:26:08 -04002474 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
2475 arg.mObject = obj;
2476 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002477 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
2478 }
2479
2480 /**
2481 * Return the WebSettings object used to control the settings for this
2482 * WebView.
2483 * @return A WebSettings object that can be used to control this WebView's
2484 * settings.
2485 */
2486 public WebSettings getSettings() {
2487 return mWebViewCore.getSettings();
2488 }
2489
2490 /**
2491 * Return the list of currently loaded plugins.
2492 * @return The list of currently loaded plugins.
Andrei Popescu385df692009-08-13 11:59:57 +01002493 *
2494 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002495 */
Andrei Popescu385df692009-08-13 11:59:57 +01002496 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002497 public static synchronized PluginList getPluginList() {
Andrei Popescu385df692009-08-13 11:59:57 +01002498 return null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002499 }
2500
2501 /**
Andrei Popescu385df692009-08-13 11:59:57 +01002502 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002503 */
Andrei Popescu385df692009-08-13 11:59:57 +01002504 @Deprecated
2505 public void refreshPlugins(boolean reloadOpenPages) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002506
2507 //-------------------------------------------------------------------------
2508 // Override View methods
2509 //-------------------------------------------------------------------------
2510
2511 @Override
2512 protected void finalize() throws Throwable {
Cary Clark9a4c0632009-08-10 16:40:08 -04002513 try {
2514 destroy();
2515 } finally {
2516 super.finalize();
2517 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002518 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002519
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002520 @Override
2521 protected void onDraw(Canvas canvas) {
2522 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
2523 if (mNativeClass == 0) {
2524 return;
2525 }
2526 if (mWebViewCore.mEndScaleZoom) {
2527 mWebViewCore.mEndScaleZoom = false;
Cary Clarkd6982c92009-05-29 11:02:22 -04002528 if (mTouchMode >= FIRST_SCROLL_ZOOM
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002529 && mTouchMode <= LAST_SCROLL_ZOOM) {
2530 setHorizontalScrollBarEnabled(true);
2531 setVerticalScrollBarEnabled(true);
2532 mTouchMode = TOUCH_DONE_MODE;
2533 }
2534 }
2535 int sc = canvas.save();
2536 if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
2537 scrollZoomDraw(canvas);
2538 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002539 // Update the buttons in the picture, so when we draw the picture
2540 // to the screen, they are in the correct state.
2541 // Tell the native side if user is a) touching the screen,
2542 // b) pressing the trackball down, or c) pressing the enter key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002543 // If the cursor is on a button, we need to draw it in the pressed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002544 // state.
2545 // If mNativeClass is 0, we should not reach here, so we do not
2546 // need to check it again.
2547 nativeRecordButtons(hasFocus() && hasWindowFocus(),
2548 mTouchMode == TOUCH_SHORTPRESS_START_MODE
Leon Scrogginse3225672009-06-03 15:53:13 -04002549 || mTrackballDown || mGotCenterDown, false);
Cary Clarkd6982c92009-05-29 11:02:22 -04002550 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002551 }
2552 canvas.restoreToCount(sc);
Cary Clarkd6982c92009-05-29 11:02:22 -04002553
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002554 if (AUTO_REDRAW_HACK && mAutoRedraw) {
2555 invalidate();
2556 }
2557 }
2558
2559 @Override
2560 public void setLayoutParams(ViewGroup.LayoutParams params) {
2561 if (params.height == LayoutParams.WRAP_CONTENT) {
2562 mWrapContent = true;
2563 }
2564 super.setLayoutParams(params);
2565 }
2566
2567 @Override
2568 public boolean performLongClick() {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04002569 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
2570 // Send the click so that the textfield is in focus
2571 // FIXME: When we start respecting changes to the native textfield's
2572 // selection, need to make sure that this does not change it.
2573 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
2574 nativeCursorNodePointer());
2575 rebuildWebTextView();
2576 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002577 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002578 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002579 } else {
2580 return super.performLongClick();
2581 }
2582 }
2583
Cary Clarkd6982c92009-05-29 11:02:22 -04002584 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002585 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002586 if (mDrawHistory) {
2587 canvas.scale(mActualScale, mActualScale);
2588 canvas.drawPicture(mHistoryPicture);
2589 return;
2590 }
2591
2592 boolean animateZoom = mZoomScale != 0;
Cary Clarkd6982c92009-05-29 11:02:22 -04002593 boolean animateScroll = !mScroller.isFinished()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002594 || mVelocityTracker != null;
2595 if (animateZoom) {
2596 float zoomScale;
2597 int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
2598 if (interval < ZOOM_ANIMATION_LENGTH) {
2599 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
Cary Clarkd6982c92009-05-29 11:02:22 -04002600 zoomScale = 1.0f / (mInvInitialZoomScale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002601 + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
2602 invalidate();
2603 } else {
2604 zoomScale = mZoomScale;
2605 // set mZoomScale to be 0 as we have done animation
2606 mZoomScale = 0;
2607 }
Grace Kloba675c7d22009-07-23 09:21:21 -07002608 float scale = zoomScale * mInvInitialZoomScale;
2609 int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
2610 - mZoomCenterX);
2611 tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
2612 * zoomScale)) + mScrollX;
2613 int ty = Math.round(scale * (mInitialScrollY + mZoomCenterY)
2614 - mZoomCenterY);
2615 ty = -pinLoc(ty, getViewHeight(), Math.round(mContentHeight
2616 * zoomScale)) + mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002617 canvas.translate(tx, ty);
2618 canvas.scale(zoomScale, zoomScale);
2619 } else {
2620 canvas.scale(mActualScale, mActualScale);
2621 }
2622
2623 mWebViewCore.drawContentPicture(canvas, color, animateZoom,
2624 animateScroll);
2625
2626 if (mNativeClass == 0) return;
2627 if (mShiftIsPressed) {
2628 if (mTouchSelection) {
2629 nativeDrawSelectionRegion(canvas);
2630 } else {
Cary Clarkd6982c92009-05-29 11:02:22 -04002631 nativeDrawSelection(canvas, mSelectX, mSelectY,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002632 mExtendSelection);
2633 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002634 } else if (drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002635 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
2636 mTouchMode = TOUCH_SHORTPRESS_MODE;
2637 HitTestResult hitTest = getHitTestResult();
2638 if (hitTest != null &&
2639 hitTest.mType != HitTestResult.UNKNOWN_TYPE) {
2640 mPrivateHandler.sendMessageDelayed(mPrivateHandler
2641 .obtainMessage(SWITCH_TO_LONGPRESS),
2642 LONG_PRESS_TIMEOUT);
2643 }
2644 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002645 nativeDrawCursorRing(canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002646 }
2647 // When the FindDialog is up, only draw the matches if we are not in
2648 // the process of scrolling them into view.
2649 if (mFindIsUp && !animateScroll) {
2650 nativeDrawMatches(canvas);
2651 }
2652 }
2653
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002654 private float scrollZoomGridScale(float invScale) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002655 float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002656 / (float) SCROLL_ZOOM_GRID;
2657 return 1.0f / griddedInvScale;
2658 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002659
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002660 private float scrollZoomX(float scale) {
2661 int width = getViewWidth();
2662 float maxScrollZoomX = mContentWidth * scale - width;
2663 int maxX = mContentWidth - width;
2664 return -(maxScrollZoomX > 0 ? mZoomScrollX * maxScrollZoomX / maxX
2665 : maxScrollZoomX / 2);
2666 }
2667
2668 private float scrollZoomY(float scale) {
2669 int height = getViewHeight();
2670 float maxScrollZoomY = mContentHeight * scale - height;
2671 int maxY = mContentHeight - height;
2672 return -(maxScrollZoomY > 0 ? mZoomScrollY * maxScrollZoomY / maxY
2673 : maxScrollZoomY / 2);
2674 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002675
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002676 private void drawMagnifyFrame(Canvas canvas, Rect frame, Paint paint) {
2677 final float ADORNMENT_LEN = 16.0f;
2678 float width = frame.width();
2679 float height = frame.height();
2680 Path path = new Path();
2681 path.moveTo(-ADORNMENT_LEN, -ADORNMENT_LEN);
2682 path.lineTo(0, 0);
2683 path.lineTo(width, 0);
2684 path.lineTo(width + ADORNMENT_LEN, -ADORNMENT_LEN);
2685 path.moveTo(-ADORNMENT_LEN, height + ADORNMENT_LEN);
2686 path.lineTo(0, height);
2687 path.lineTo(width, height);
2688 path.lineTo(width + ADORNMENT_LEN, height + ADORNMENT_LEN);
2689 path.moveTo(0, 0);
2690 path.lineTo(0, height);
2691 path.moveTo(width, 0);
2692 path.lineTo(width, height);
2693 path.offset(frame.left, frame.top);
2694 canvas.drawPath(path, paint);
2695 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002696
2697 // Returns frame surrounding magified portion of screen while
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002698 // scroll-zoom is enabled. The frame is also used to center the
2699 // zoom-in zoom-out points at the start and end of the animation.
2700 private Rect scrollZoomFrame(int width, int height, float halfScale) {
2701 Rect scrollFrame = new Rect();
Cary Clarkd6982c92009-05-29 11:02:22 -04002702 scrollFrame.set(mZoomScrollX, mZoomScrollY,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002703 mZoomScrollX + width, mZoomScrollY + height);
2704 if (mContentWidth * mZoomScrollLimit < width) {
2705 float scale = zoomFrameScaleX(width, halfScale, 1.0f);
2706 float offsetX = (width * scale - width) * 0.5f;
2707 scrollFrame.left -= offsetX;
2708 scrollFrame.right += offsetX;
2709 }
2710 if (mContentHeight * mZoomScrollLimit < height) {
2711 float scale = zoomFrameScaleY(height, halfScale, 1.0f);
2712 float offsetY = (height * scale - height) * 0.5f;
2713 scrollFrame.top -= offsetY;
2714 scrollFrame.bottom += offsetY;
2715 }
2716 return scrollFrame;
2717 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002718
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002719 private float zoomFrameScaleX(int width, float halfScale, float noScale) {
2720 // mContentWidth > width > mContentWidth * mZoomScrollLimit
2721 if (mContentWidth <= width) {
2722 return halfScale;
2723 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002724 float part = (width - mContentWidth * mZoomScrollLimit)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002725 / (width * (1 - mZoomScrollLimit));
2726 return halfScale * part + noScale * (1.0f - part);
2727 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002728
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002729 private float zoomFrameScaleY(int height, float halfScale, float noScale) {
2730 if (mContentHeight <= height) {
2731 return halfScale;
2732 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002733 float part = (height - mContentHeight * mZoomScrollLimit)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002734 / (height * (1 - mZoomScrollLimit));
2735 return halfScale * part + noScale * (1.0f - part);
2736 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002737
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002738 private float scrollZoomMagScale(float invScale) {
2739 return (invScale * 2 + mInvActualScale) / 3;
2740 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002741
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002742 private void scrollZoomDraw(Canvas canvas) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002743 float invScale = mZoomScrollInvLimit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002744 int elapsed = 0;
2745 if (mTouchMode != SCROLL_ZOOM_OUT) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002746 elapsed = (int) Math.min(System.currentTimeMillis()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002747 - mZoomScrollStart, SCROLL_ZOOM_DURATION);
Cary Clarkd6982c92009-05-29 11:02:22 -04002748 float transitionScale = (mZoomScrollInvLimit - mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002749 * elapsed / SCROLL_ZOOM_DURATION;
2750 if (mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
2751 invScale = mInvActualScale + transitionScale;
2752 } else { /* if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) */
2753 invScale = mZoomScrollInvLimit - transitionScale;
2754 }
2755 }
2756 float scale = scrollZoomGridScale(invScale);
2757 invScale = 1.0f / scale;
2758 int width = getViewWidth();
2759 int height = getViewHeight();
2760 float halfScale = scrollZoomMagScale(invScale);
2761 Rect scrollFrame = scrollZoomFrame(width, height, halfScale);
2762 if (elapsed == SCROLL_ZOOM_DURATION) {
2763 if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
2764 setHorizontalScrollBarEnabled(true);
2765 setVerticalScrollBarEnabled(true);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002766 rebuildWebTextView();
Cary Clarkd6982c92009-05-29 11:02:22 -04002767 scrollTo((int) (scrollFrame.centerX() * mActualScale)
2768 - (width >> 1), (int) (scrollFrame.centerY()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002769 * mActualScale) - (height >> 1));
2770 mTouchMode = TOUCH_DONE_MODE;
Patrick Scott0a5ce012009-07-02 08:56:10 -04002771 // Show all the child views once we are done.
2772 mViewManager.showAll();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002773 } else {
2774 mTouchMode = SCROLL_ZOOM_OUT;
2775 }
2776 }
2777 float newX = scrollZoomX(scale);
2778 float newY = scrollZoomY(scale);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002779 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002780 Log.v(LOGTAG, "scrollZoomDraw scale=" + scale + " + (" + newX
2781 + ", " + newY + ") mZoomScroll=(" + mZoomScrollX + ", "
Cary Clarkd6982c92009-05-29 11:02:22 -04002782 + mZoomScrollY + ")" + " invScale=" + invScale + " scale="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002783 + scale);
2784 }
2785 canvas.translate(newX, newY);
2786 canvas.scale(scale, scale);
2787 boolean animating = mTouchMode != SCROLL_ZOOM_OUT;
2788 if (mDrawHistory) {
2789 int sc = canvas.save(Canvas.CLIP_SAVE_FLAG);
2790 Rect clip = new Rect(0, 0, mHistoryPicture.getWidth(),
2791 mHistoryPicture.getHeight());
2792 canvas.clipRect(clip, Region.Op.DIFFERENCE);
2793 canvas.drawColor(mBackgroundColor);
2794 canvas.restoreToCount(sc);
2795 canvas.drawPicture(mHistoryPicture);
2796 } else {
2797 mWebViewCore.drawContentPicture(canvas, mBackgroundColor,
2798 animating, true);
2799 }
2800 if (mTouchMode == TOUCH_DONE_MODE) {
2801 return;
2802 }
2803 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
2804 paint.setStyle(Paint.Style.STROKE);
2805 paint.setStrokeWidth(30.0f);
2806 paint.setARGB(0x50, 0, 0, 0);
2807 int maxX = mContentWidth - width;
2808 int maxY = mContentHeight - height;
2809 if (true) { // experiment: draw hint to place finger off magnify area
2810 drawMagnifyFrame(canvas, scrollFrame, paint);
2811 } else {
2812 canvas.drawRect(scrollFrame, paint);
2813 }
2814 int sc = canvas.save();
2815 canvas.clipRect(scrollFrame);
2816 float halfX = (float) mZoomScrollX / maxX;
2817 if (mContentWidth * mZoomScrollLimit < width) {
2818 halfX = zoomFrameScaleX(width, 0.5f, halfX);
2819 }
2820 float halfY = (float) mZoomScrollY / maxY;
2821 if (mContentHeight * mZoomScrollLimit < height) {
2822 halfY = zoomFrameScaleY(height, 0.5f, halfY);
2823 }
2824 canvas.scale(halfScale, halfScale, mZoomScrollX + width * halfX
2825 , mZoomScrollY + height * halfY);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002826 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002827 Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=("
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002828 + width + ", " + height + ") half=(" + halfX + ", "
2829 + halfY + ")");
2830 }
2831 if (mDrawHistory) {
2832 canvas.drawPicture(mHistoryPicture);
2833 } else {
2834 mWebViewCore.drawContentPicture(canvas, mBackgroundColor,
2835 animating, false);
2836 }
2837 canvas.restoreToCount(sc);
2838 if (mTouchMode != SCROLL_ZOOM_OUT) {
2839 invalidate();
2840 }
2841 }
2842
2843 private void zoomScrollTap(float x, float y) {
2844 float scale = scrollZoomGridScale(mZoomScrollInvLimit);
2845 float left = scrollZoomX(scale);
2846 float top = scrollZoomY(scale);
2847 int width = getViewWidth();
2848 int height = getViewHeight();
2849 x -= width * scale / 2;
2850 y -= height * scale / 2;
2851 mZoomScrollX = Math.min(mContentWidth - width
2852 , Math.max(0, (int) ((x - left) / scale)));
2853 mZoomScrollY = Math.min(mContentHeight - height
2854 , Math.max(0, (int) ((y - top) / scale)));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002855 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002856 Log.v(LOGTAG, "zoomScrollTap scale=" + scale + " + (" + left
2857 + ", " + top + ") mZoomScroll=(" + mZoomScrollX + ", "
2858 + mZoomScrollY + ")" + " x=" + x + " y=" + y);
2859 }
2860 }
2861
Leon Scrogginsa5645b22009-06-09 15:31:19 -04002862 /**
2863 * @hide
2864 */
2865 public boolean canZoomScrollOut() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002866 if (mContentWidth == 0 || mContentHeight == 0) {
2867 return false;
2868 }
2869 int width = getViewWidth();
2870 int height = getViewHeight();
2871 float x = (float) width / (float) mContentWidth;
2872 float y = (float) height / (float) mContentHeight;
2873 mZoomScrollLimit = Math.max(DEFAULT_MIN_ZOOM_SCALE, Math.min(x, y));
2874 mZoomScrollInvLimit = 1.0f / mZoomScrollLimit;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002875 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002876 Log.v(LOGTAG, "canZoomScrollOut"
2877 + " mInvActualScale=" + mInvActualScale
2878 + " mZoomScrollLimit=" + mZoomScrollLimit
2879 + " mZoomScrollInvLimit=" + mZoomScrollInvLimit
2880 + " mContentWidth=" + mContentWidth
2881 + " mContentHeight=" + mContentHeight
2882 );
2883 }
2884 // don't zoom out unless magnify area is at least half as wide
2885 // or tall as content
2886 float limit = mZoomScrollLimit * 2;
2887 return mContentWidth >= width * limit
2888 || mContentHeight >= height * limit;
2889 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002890
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002891 private void startZoomScrollOut() {
2892 setHorizontalScrollBarEnabled(false);
2893 setVerticalScrollBarEnabled(false);
The Android Open Source Project10592532009-03-18 17:39:46 -07002894 if (getSettings().getBuiltInZoomControls()) {
2895 if (mZoomButtonsController.isVisible()) {
2896 mZoomButtonsController.setVisible(false);
2897 }
2898 } else {
2899 if (mZoomControlRunnable != null) {
2900 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
2901 }
2902 if (mZoomControls != null) {
2903 mZoomControls.hide();
2904 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002905 }
2906 int width = getViewWidth();
2907 int height = getViewHeight();
2908 int halfW = width >> 1;
2909 mLastTouchX = halfW;
2910 int halfH = height >> 1;
2911 mLastTouchY = halfH;
2912 mScroller.abortAnimation();
2913 mZoomScrollStart = System.currentTimeMillis();
2914 Rect zoomFrame = scrollZoomFrame(width, height
2915 , scrollZoomMagScale(mZoomScrollInvLimit));
Cary Clarkd6982c92009-05-29 11:02:22 -04002916 mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002917 - (zoomFrame.width() >> 1));
Cary Clarkd6982c92009-05-29 11:02:22 -04002918 mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002919 - (zoomFrame.height() >> 1));
2920 scrollTo(0, 0); // triggers inval, starts animation
2921 clearTextEntry();
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002922 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002923 Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=("
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002924 + mZoomScrollX + ", " + mZoomScrollY +")");
2925 }
2926 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002927
Leon Scrogginsa5645b22009-06-09 15:31:19 -04002928 /**
2929 * @hide
2930 */
2931 public void zoomScrollOut() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002932 if (canZoomScrollOut() == false) {
2933 mTouchMode = TOUCH_DONE_MODE;
2934 return;
2935 }
Patrick Scott0a5ce012009-07-02 08:56:10 -04002936 // Hide the child views while in this mode.
2937 mViewManager.hideAll();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002938 startZoomScrollOut();
2939 mTouchMode = SCROLL_ZOOM_ANIMATION_OUT;
2940 invalidate();
2941 }
2942
2943 private void moveZoomScrollWindow(float x, float y) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002944 if (Math.abs(x - mLastZoomScrollRawX) < 1.5f
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002945 && Math.abs(y - mLastZoomScrollRawY) < 1.5f) {
2946 return;
2947 }
2948 mLastZoomScrollRawX = x;
2949 mLastZoomScrollRawY = y;
2950 int oldX = mZoomScrollX;
2951 int oldY = mZoomScrollY;
2952 int width = getViewWidth();
2953 int height = getViewHeight();
2954 int maxZoomX = mContentWidth - width;
2955 if (maxZoomX > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002956 int maxScreenX = width - (int) Math.ceil(width
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002957 * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002958 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002959 Log.v(LOGTAG, "moveZoomScrollWindow-X"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002960 + " maxScreenX=" + maxScreenX + " width=" + width
Cary Clarkd6982c92009-05-29 11:02:22 -04002961 + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002962 }
2963 x += maxScreenX * mLastScrollX / maxZoomX - mLastTouchX;
2964 x *= Math.max(maxZoomX / maxScreenX, mZoomScrollInvLimit);
2965 mZoomScrollX = Math.max(0, Math.min(maxZoomX, (int) x));
2966 }
2967 int maxZoomY = mContentHeight - height;
2968 if (maxZoomY > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002969 int maxScreenY = height - (int) Math.ceil(height
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002970 * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002971 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002972 Log.v(LOGTAG, "moveZoomScrollWindow-Y"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002973 + " maxScreenY=" + maxScreenY + " height=" + height
Cary Clarkd6982c92009-05-29 11:02:22 -04002974 + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002975 }
2976 y += maxScreenY * mLastScrollY / maxZoomY - mLastTouchY;
2977 y *= Math.max(maxZoomY / maxScreenY, mZoomScrollInvLimit);
2978 mZoomScrollY = Math.max(0, Math.min(maxZoomY, (int) y));
2979 }
2980 if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
2981 invalidate();
2982 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002983 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002984 Log.v(LOGTAG, "moveZoomScrollWindow"
2985 + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")"
2986 + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")"
2987 + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")"
2988 + " last=("+mLastScrollX+", "+mLastScrollY+")"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002989 + " x=" + x + " y=" + y);
2990 }
2991 }
2992
2993 private void setZoomScrollIn() {
2994 mZoomScrollStart = System.currentTimeMillis();
2995 }
2996
2997 private float mZoomScrollLimit;
2998 private float mZoomScrollInvLimit;
2999 private int mLastScrollX;
3000 private int mLastScrollY;
3001 private long mZoomScrollStart;
3002 private int mZoomScrollX;
3003 private int mZoomScrollY;
3004 private float mLastZoomScrollRawX = -1000.0f;
3005 private float mLastZoomScrollRawY = -1000.0f;
3006 // The zoomed scale varies from 1.0 to DEFAULT_MIN_ZOOM_SCALE == 0.25.
3007 // The zoom animation duration SCROLL_ZOOM_DURATION == 0.5.
3008 // Two pressures compete for gridding; a high frame rate (e.g. 20 fps)
3009 // and minimizing font cache allocations (fewer frames is better).
3010 // A SCROLL_ZOOM_GRID of 6 permits about 20 zoom levels over 0.5 seconds:
3011 // the inverse of: 1.0, 1.16, 1.33, 1.5, 1.67, 1.84, 2.0, etc. to 4.0
3012 private static final int SCROLL_ZOOM_GRID = 6;
3013 private static final int SCROLL_ZOOM_DURATION = 500;
3014 // Make it easier to get to the bottom of a document by reserving a 32
3015 // pixel buffer, for when the starting drag is a bit below the bottom of
3016 // the magnify frame.
3017 private static final int SCROLL_ZOOM_FINGER_BUFFER = 32;
3018
3019 // draw history
3020 private boolean mDrawHistory = false;
3021 private Picture mHistoryPicture = null;
3022 private int mHistoryWidth = 0;
3023 private int mHistoryHeight = 0;
3024
3025 // Only check the flag, can be called from WebCore thread
3026 boolean drawHistory() {
3027 return mDrawHistory;
3028 }
3029
3030 // Should only be called in UI thread
3031 void switchOutDrawHistory() {
3032 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Cary Clarkbc2e33b2009-04-23 13:12:19 -04003033 if (mDrawHistory && mWebViewCore.pictureReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003034 mDrawHistory = false;
3035 invalidate();
3036 int oldScrollX = mScrollX;
3037 int oldScrollY = mScrollY;
3038 mScrollX = pinLocX(mScrollX);
3039 mScrollY = pinLocY(mScrollY);
3040 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
3041 mUserScroll = false;
3042 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
3043 oldScrollY);
3044 }
3045 sendOurVisibleRect();
3046 }
3047 }
3048
Cary Clarkd6982c92009-05-29 11:02:22 -04003049 WebViewCore.CursorData cursorData() {
3050 WebViewCore.CursorData result = new WebViewCore.CursorData();
3051 result.mMoveGeneration = nativeMoveGeneration();
3052 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04003053 Point position = nativeCursorPosition();
3054 result.mX = position.x;
3055 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04003056 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003057 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003059 /**
3060 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04003061 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003062 * order, swap them.
3063 * @param start Beginning of selection to delete.
3064 * @param end End of selection to delete.
3065 */
3066 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003067 mTextGeneration++;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04003068 WebViewCore.TextSelectionData data
3069 = new WebViewCore.TextSelectionData(start, end);
3070 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
3071 data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003072 }
3073
3074 /**
3075 * Set the selection to (start, end) in the focused textfield. If start and
3076 * end are out of order, swap them.
3077 * @param start Beginning of selection.
3078 * @param end End of selection.
3079 */
3080 /* package */ void setSelection(int start, int end) {
Cary Clark2f1d60c2009-06-03 08:05:53 -04003081 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003082 }
3083
3084 // Called by JNI when a touch event puts a textfield into focus.
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003085 private void displaySoftKeyboard(boolean isTextView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003086 InputMethodManager imm = (InputMethodManager)
3087 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003088
3089 if (isTextView) {
3090 imm.showSoftInput(mWebTextView, 0);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003091 // Now we need to fake a touch event to place the cursor where the
3092 // user touched.
3093 AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
3094 mWebTextView.getLayoutParams();
3095 if (lp != null) {
3096 // Take the last touch and adjust for the location of the
3097 // WebTextView.
3098 float x = mLastTouchX + (float) (mScrollX - lp.x);
3099 float y = mLastTouchY + (float) (mScrollY - lp.y);
3100 mWebTextView.fakeTouchEvent(x, y);
3101 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003102 if (mInZoomOverview) {
3103 // if in zoom overview mode, call doDoubleTap() to bring it back
3104 // to normal mode so that user can enter text.
3105 doDoubleTap();
3106 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003107 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003108 else { // used by plugins
3109 imm.showSoftInput(this, 0);
3110 }
3111 }
3112
3113 // Called by WebKit to instruct the UI to hide the keyboard
3114 private void hideSoftKeyboard() {
3115 InputMethodManager imm = (InputMethodManager)
3116 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
3117
3118 imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003119 }
3120
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003121 /*
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003122 * This method checks the current focus and cursor and potentially rebuilds
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003123 * mWebTextView to have the appropriate properties, such as password,
3124 * multiline, and what text it contains. It also removes it if necessary.
3125 */
Leon Scroggins01058282009-07-30 16:33:56 -04003126 /* package */ void rebuildWebTextView() {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003127 // If the WebView does not have focus, do nothing until it gains focus.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003128 if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())
Cary Clarkd6982c92009-05-29 11:02:22 -04003129 || (mTouchMode >= FIRST_SCROLL_ZOOM
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003130 && mTouchMode <= LAST_SCROLL_ZOOM)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003131 return;
3132 }
3133 boolean alreadyThere = inEditingMode();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003134 // inEditingMode can only return true if mWebTextView is non-null,
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003135 // so we can safely call remove() if (alreadyThere)
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003136 if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003137 if (alreadyThere) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003138 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003139 }
3140 return;
3141 }
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003142 // At this point, we know we have found an input field, so go ahead
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003143 // and create the WebTextView if necessary.
3144 if (mWebTextView == null) {
3145 mWebTextView = new WebTextView(mContext, WebView.this);
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003146 // Initialize our generation number.
3147 mTextGeneration = 0;
3148 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003149 mWebTextView.setTextSize(contentToView(nativeFocusCandidateTextSize()));
Cary Clark3524be92009-06-22 13:09:11 -04003150 Rect visibleRect = new Rect();
3151 calcOurContentVisibleRect(visibleRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003152 // Note that sendOurVisibleRect calls viewToContent, so the coordinates
3153 // should be in content coordinates.
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003154 Rect bounds = nativeFocusCandidateNodeBounds();
Cary Clarkd6982c92009-05-29 11:02:22 -04003155 if (!Rect.intersects(bounds, visibleRect)) {
Leon Scroggins40981262009-07-01 10:57:47 -04003156 mWebTextView.bringIntoView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003157 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003158 String text = nativeFocusCandidateText();
3159 int nodePointer = nativeFocusCandidatePointer();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003160 if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003161 // It is possible that we have the same textfield, but it has moved,
3162 // i.e. In the case of opening/closing the screen.
3163 // In that case, we need to set the dimensions, but not the other
3164 // aspects.
3165 // We also need to restore the selection, which gets wrecked by
3166 // calling setTextEntryRect.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003167 Spannable spannable = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003168 int start = Selection.getSelectionStart(spannable);
3169 int end = Selection.getSelectionEnd(spannable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003170 // If the text has been changed by webkit, update it. However, if
3171 // there has been more UI text input, ignore it. We will receive
3172 // another update when that text is recognized.
Cary Clarkd6982c92009-05-29 11:02:22 -04003173 if (text != null && !text.equals(spannable.toString())
3174 && nativeTextGeneration() == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003175 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003176 } else {
3177 Selection.setSelection(spannable, start, end);
3178 }
3179 } else {
Cary Clarkd6982c92009-05-29 11:02:22 -04003180 Rect vBox = contentToView(bounds);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003181 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
3182 vBox.height());
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003183 mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
3184 Gravity.RIGHT : Gravity.NO_GRAVITY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003185 // this needs to be called before update adapter thread starts to
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003186 // ensure the mWebTextView has the same node pointer
3187 mWebTextView.setNodePointer(nodePointer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003188 int maxLength = -1;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003189 boolean isTextField = nativeFocusCandidateIsTextField();
Cary Clarkd6982c92009-05-29 11:02:22 -04003190 if (isTextField) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003191 maxLength = nativeFocusCandidateMaxLength();
3192 String name = nativeFocusCandidateName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003193 if (mWebViewCore.getSettings().getSaveFormData()
Cary Clarkd6982c92009-05-29 11:02:22 -04003194 && name != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003195 Message update = mPrivateHandler.obtainMessage(
Cary Clarkded054c2009-06-15 10:26:08 -04003196 REQUEST_FORM_DATA, nodePointer);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003197 RequestFormData updater = new RequestFormData(name,
3198 getUrl(), update);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003199 Thread t = new Thread(updater);
3200 t.start();
3201 }
3202 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003203 mWebTextView.setMaxLength(maxLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003204 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003205 mWebTextView.setAdapterCustom(adapter);
3206 mWebTextView.setSingleLine(isTextField);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003207 mWebTextView.setInPassword(nativeFocusCandidateIsPassword());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003208 if (null == text) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003209 mWebTextView.setText("", 0, 0);
Cary Clark243ea062009-06-25 10:49:32 -04003210 if (DebugFlags.WEB_VIEW) {
3211 Log.v(LOGTAG, "rebuildWebTextView null == text");
3212 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003213 } else {
3214 // Change to true to enable the old style behavior, where
3215 // entering a textfield/textarea always set the selection to the
3216 // whole field. This was desirable for the case where the user
3217 // intends to scroll past the field using the trackball.
3218 // However, it causes a problem when replying to emails - the
3219 // user expects the cursor to be at the beginning of the
3220 // textarea. Testing out a new behavior, where textfields set
3221 // selection at the end, and textareas at the beginning.
3222 if (false) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003223 mWebTextView.setText(text, 0, text.length());
Cary Clarkd6982c92009-05-29 11:02:22 -04003224 } else if (isTextField) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003225 int length = text.length();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003226 mWebTextView.setText(text, length, length);
Cary Clark243ea062009-06-25 10:49:32 -04003227 if (DebugFlags.WEB_VIEW) {
3228 Log.v(LOGTAG, "rebuildWebTextView length=" + length);
3229 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003230 } else {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003231 mWebTextView.setText(text, 0, 0);
Cary Clark243ea062009-06-25 10:49:32 -04003232 if (DebugFlags.WEB_VIEW) {
3233 Log.v(LOGTAG, "rebuildWebTextView !isTextField");
3234 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003235 }
3236 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003237 mWebTextView.requestFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003238 }
3239 }
3240
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003241 /*
3242 * This class requests an Adapter for the WebTextView which shows past
3243 * entries stored in the database. It is a Runnable so that it can be done
3244 * in its own thread, without slowing down the UI.
3245 */
3246 private class RequestFormData implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003247 private String mName;
3248 private String mUrl;
3249 private Message mUpdateMessage;
3250
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003251 public RequestFormData(String name, String url, Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003252 mName = name;
3253 mUrl = url;
3254 mUpdateMessage = msg;
3255 }
3256
3257 public void run() {
3258 ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
3259 if (pastEntries.size() > 0) {
3260 AutoCompleteAdapter adapter = new
3261 AutoCompleteAdapter(mContext, pastEntries);
Cary Clarkded054c2009-06-15 10:26:08 -04003262 mUpdateMessage.obj = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003263 mUpdateMessage.sendToTarget();
3264 }
3265 }
3266 }
3267
Leon Scrogginse3225672009-06-03 15:53:13 -04003268 // This is used to determine long press with the center key. Does not
3269 // affect long press with the trackball/touch.
3270 private boolean mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003271
3272 @Override
3273 public boolean onKeyDown(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003274 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003275 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003276 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003277 }
3278
3279 if (mNativeClass == 0) {
3280 return false;
3281 }
3282
3283 // do this hack up front, so it always works, regardless of touch-mode
3284 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
3285 mAutoRedraw = !mAutoRedraw;
3286 if (mAutoRedraw) {
3287 invalidate();
3288 }
3289 return true;
3290 }
3291
3292 // Bubble up the key event if
3293 // 1. it is a system key; or
3294 // 2. the host application wants to handle it; or
3295 // 3. webview is in scroll-zoom state;
3296 if (event.isSystem()
3297 || mCallbackProxy.uiOverrideKeyEvent(event)
3298 || (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM)) {
3299 return false;
3300 }
3301
Cary Clark2f1d60c2009-06-03 08:05:53 -04003302 if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
Cary Clarkd6982c92009-05-29 11:02:22 -04003303 && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003304 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
3305 mExtendSelection = false;
3306 mShiftIsPressed = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04003307 if (nativeHasCursorNode()) {
3308 Rect rect = nativeCursorNodeBounds();
3309 mSelectX = contentToView(rect.left);
3310 mSelectY = contentToView(rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003311 } else {
3312 mSelectX = mScrollX + (int) mLastTouchX;
3313 mSelectY = mScrollY + (int) mLastTouchY;
3314 }
Cary Clarke872f3a2009-06-11 09:51:11 -04003315 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003316 }
3317
3318 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3319 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3320 // always handle the navigation keys in the UI thread
3321 switchOutDrawHistory();
Cary Clark215b72c2009-06-26 14:38:43 -04003322 if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003323 playSoundEffect(keyCodeToSoundsEffect(keyCode));
3324 return true;
3325 }
3326 // Bubble up the key event as WebView doesn't handle it
3327 return false;
3328 }
3329
Leon Scrogginse3225672009-06-03 15:53:13 -04003330 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003331 switchOutDrawHistory();
3332 if (event.getRepeatCount() == 0) {
Leon Scrogginse3225672009-06-03 15:53:13 -04003333 mGotCenterDown = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003334 mPrivateHandler.sendMessageDelayed(mPrivateHandler
Leon Scrogginse3225672009-06-03 15:53:13 -04003335 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003336 // Already checked mNativeClass, so we do not need to check it
3337 // again.
3338 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
3339 return true;
3340 }
3341 // Bubble up the key event as WebView doesn't handle it
3342 return false;
3343 }
3344
Cary Clark843bbb82009-04-20 16:03:31 -04003345 if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
3346 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
3347 // turn off copy select if a shift-key combo is pressed
3348 mExtendSelection = mShiftIsPressed = false;
3349 if (mTouchMode == TOUCH_SELECT_MODE) {
3350 mTouchMode = TOUCH_INIT_MODE;
3351 }
3352 }
3353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003354 if (getSettings().getNavDump()) {
3355 switch (keyCode) {
3356 case KeyEvent.KEYCODE_4:
3357 // "/data/data/com.android.browser/displayTree.txt"
3358 nativeDumpDisplayTree(getUrl());
3359 break;
3360 case KeyEvent.KEYCODE_5:
3361 case KeyEvent.KEYCODE_6:
3362 // 5: dump the dom tree to the file
3363 // "/data/data/com.android.browser/domTree.txt"
3364 // 6: dump the dom tree to the adb log
3365 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE,
3366 (keyCode == KeyEvent.KEYCODE_5) ? 1 : 0, 0);
3367 break;
3368 case KeyEvent.KEYCODE_7:
3369 case KeyEvent.KEYCODE_8:
3370 // 7: dump the render tree to the file
3371 // "/data/data/com.android.browser/renderTree.txt"
3372 // 8: dump the render tree to the adb log
3373 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE,
3374 (keyCode == KeyEvent.KEYCODE_7) ? 1 : 0, 0);
3375 break;
3376 case KeyEvent.KEYCODE_9:
3377 nativeInstrumentReport();
3378 return true;
3379 }
3380 }
3381
Cary Clark215b72c2009-06-26 14:38:43 -04003382 if (nativeCursorIsPlugin()) {
3383 nativeUpdatePluginReceivesEvents();
3384 invalidate();
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003385 } else if (nativeCursorIsTextInput()) {
Leon Scroggins1cc24202009-06-16 10:10:28 -04003386 // This message will put the node in focus, for the DOM's notion
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003387 // of focus, and make the focuscontroller active
Leon Scroggins01058282009-07-30 16:33:56 -04003388 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
3389 nativeCursorNodePointer());
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003390 // This will bring up the WebTextView and put it in focus, for
3391 // our view system's notion of focus
3392 rebuildWebTextView();
3393 // Now we need to pass the event to it
3394 return mWebTextView.onKeyDown(keyCode, event);
Leon Scroggins40981262009-07-01 10:57:47 -04003395 } else if (nativeHasFocusNode()) {
3396 // In this case, the cursor is not on a text input, but the focus
3397 // might be. Check it, and if so, hand over to the WebTextView.
3398 rebuildWebTextView();
3399 if (inEditingMode()) {
3400 return mWebTextView.onKeyDown(keyCode, event);
3401 }
Cary Clark19436562009-06-04 16:25:07 -04003402 }
3403
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003404 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003405 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003406 // pass the key to DOM
3407 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
3408 // return true as DOM handles the key
3409 return true;
3410 }
3411
3412 // Bubble up the key event as WebView doesn't handle it
3413 return false;
3414 }
3415
3416 @Override
3417 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003418 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003419 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003420 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003421 }
3422
3423 if (mNativeClass == 0) {
3424 return false;
3425 }
3426
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003427 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04003428 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
3429 String text = nativeCursorText();
3430 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003431 && text.startsWith(SCHEME_TEL)) {
3432 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
3433 getContext().startActivity(intent);
3434 return true;
3435 }
3436 }
3437
3438 // Bubble up the key event if
3439 // 1. it is a system key; or
3440 // 2. the host application wants to handle it;
3441 if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
3442 return false;
3443 }
3444
3445 // special handling in scroll_zoom state
3446 if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
3447 if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode
3448 && mTouchMode != SCROLL_ZOOM_ANIMATION_IN) {
3449 setZoomScrollIn();
3450 mTouchMode = SCROLL_ZOOM_ANIMATION_IN;
3451 invalidate();
3452 return true;
3453 }
3454 return false;
3455 }
3456
Cary Clarkd6982c92009-05-29 11:02:22 -04003457 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003458 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
3459 if (commitCopy()) {
3460 return true;
3461 }
3462 }
3463
3464 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3465 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3466 // always handle the navigation keys in the UI thread
3467 // Bubble up the key event as WebView doesn't handle it
3468 return false;
3469 }
3470
Leon Scrogginse3225672009-06-03 15:53:13 -04003471 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003472 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04003473 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
3474 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003475
Leon Scrogginse3225672009-06-03 15:53:13 -04003476 if (mShiftIsPressed) {
3477 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003478 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003479 if (getSettings().supportZoom()
3480 && mTouchMode == TOUCH_DOUBLECLICK_MODE) {
3481 zoomScrollOut();
3482 } else {
3483 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3484 .obtainMessage(SWITCH_TO_CLICK), TAP_TIMEOUT);
3485 if (DebugFlags.WEB_VIEW) {
3486 Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
3487 }
3488 mTouchMode = TOUCH_DOUBLECLICK_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003489 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003490 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003491 }
3492
3493 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003494 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003495 // pass the key to DOM
3496 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
3497 // return true as DOM handles the key
3498 return true;
3499 }
3500
3501 // Bubble up the key event as WebView doesn't handle it
3502 return false;
3503 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003504
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003505 /**
3506 * @hide
3507 */
3508 public void emulateShiftHeld() {
3509 mExtendSelection = false;
3510 mShiftIsPressed = true;
Cary Clarke872f3a2009-06-11 09:51:11 -04003511 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003512 }
3513
3514 private boolean commitCopy() {
3515 boolean copiedSomething = false;
3516 if (mExtendSelection) {
3517 // copy region so core operates on copy without touching orig.
3518 Region selection = new Region(nativeGetSelection());
3519 if (selection.isEmpty() == false) {
3520 Toast.makeText(mContext
3521 , com.android.internal.R.string.text_copied
3522 , Toast.LENGTH_SHORT).show();
3523 mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection);
3524 copiedSomething = true;
3525 }
3526 mExtendSelection = false;
3527 }
3528 mShiftIsPressed = false;
3529 if (mTouchMode == TOUCH_SELECT_MODE) {
3530 mTouchMode = TOUCH_INIT_MODE;
3531 }
3532 return copiedSomething;
3533 }
3534
3535 // Set this as a hierarchy change listener so we can know when this view
3536 // is removed and still have access to our parent.
3537 @Override
3538 protected void onAttachedToWindow() {
3539 super.onAttachedToWindow();
3540 ViewParent parent = getParent();
3541 if (parent instanceof ViewGroup) {
3542 ViewGroup p = (ViewGroup) parent;
3543 p.setOnHierarchyChangeListener(this);
3544 }
3545 }
3546
3547 @Override
3548 protected void onDetachedFromWindow() {
3549 super.onDetachedFromWindow();
3550 ViewParent parent = getParent();
3551 if (parent instanceof ViewGroup) {
3552 ViewGroup p = (ViewGroup) parent;
3553 p.setOnHierarchyChangeListener(null);
3554 }
3555
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003556 // Clean up the zoom controller
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003557 mZoomButtonsController.setVisible(false);
3558 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003559
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003560 // Implementation for OnHierarchyChangeListener
3561 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04003562
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003563 public void onChildViewRemoved(View p, View child) {
3564 if (child == this) {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003565 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003566 }
3567 }
3568
3569 /**
3570 * @deprecated WebView should not have implemented
3571 * ViewTreeObserver.OnGlobalFocusChangeListener. This method
3572 * does nothing now.
3573 */
3574 @Deprecated
3575 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
3576 }
3577
Cary Clarkd6982c92009-05-29 11:02:22 -04003578 // To avoid drawing the cursor ring, and remove the TextView when our window
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003579 // loses focus.
3580 @Override
3581 public void onWindowFocusChanged(boolean hasWindowFocus) {
3582 if (hasWindowFocus) {
3583 if (hasFocus()) {
3584 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003585 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04003586 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003587 if (mNativeClass != 0) {
3588 nativeRecordButtons(true, false, true);
Leon Scroggins8cdad882009-06-30 08:47:04 -04003589 if (inEditingMode()) {
3590 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
3591 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003592 }
3593 } else {
3594 // If our window gained focus, but we do not have it, do not
Cary Clarkd6982c92009-05-29 11:02:22 -04003595 // draw the cursor ring.
3596 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003597 // We do not call nativeRecordButtons here because we assume
3598 // that when we lost focus, or window focus, it got called with
3599 // false for the first parameter
3600 }
3601 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07003602 if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003603 /*
3604 * The zoom controls come in their own window, so our window
Cary Clarkd6982c92009-05-29 11:02:22 -04003605 * loses focus. Our policy is to not draw the cursor ring if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003606 * our window is not focused, but this is an exception since
3607 * the user can still navigate the web page with the zoom
3608 * controls showing.
3609 */
Cary Clarkd6982c92009-05-29 11:02:22 -04003610 // If our window has lost focus, stop drawing the cursor ring
3611 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003612 }
3613 mGotKeyDown = false;
3614 mShiftIsPressed = false;
3615 if (mNativeClass != 0) {
3616 nativeRecordButtons(false, false, true);
3617 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003618 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003619 }
3620 invalidate();
3621 super.onWindowFocusChanged(hasWindowFocus);
3622 }
3623
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003624 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003625 * Pass a message to WebCore Thread, telling the WebCore::Page's
3626 * FocusController to be "inactive" so that it will
3627 * not draw the blinking cursor. It gets set to "active" to draw the cursor
3628 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003629 */
Leon Scroggins01058282009-07-30 16:33:56 -04003630 /* package */ void setFocusControllerInactive() {
Leon Scrogginsfd06bc82009-06-08 13:22:02 -04003631 // Do not need to also check whether mWebViewCore is null, because
3632 // mNativeClass is only set if mWebViewCore is non null
3633 if (mNativeClass == 0) return;
Leon Scroggins8cdad882009-06-30 08:47:04 -04003634 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003635 }
3636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003637 @Override
3638 protected void onFocusChanged(boolean focused, int direction,
3639 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003640 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003641 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
3642 }
3643 if (focused) {
3644 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003645 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003646 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003647 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003648 if (mNativeClass != 0) {
3649 nativeRecordButtons(true, false, true);
3650 }
3651 //} else {
3652 // The WebView has gained focus while we do not have
3653 // windowfocus. When our window lost focus, we should have
3654 // called nativeRecordButtons(false...)
3655 }
3656 } else {
3657 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04003658 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003659 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003660 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003661 if (mNativeClass != 0) {
3662 nativeRecordButtons(false, false, true);
3663 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003664 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003665 }
3666 mGotKeyDown = false;
3667 }
3668
3669 super.onFocusChanged(focused, direction, previouslyFocusedRect);
3670 }
3671
3672 @Override
3673 protected void onSizeChanged(int w, int h, int ow, int oh) {
3674 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003675 // Center zooming to the center of the screen.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003676 mZoomCenterX = getViewWidth() * .5f;
3677 mZoomCenterY = getViewHeight() * .5f;
3678
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003679 // update mMinZoomScale if the minimum zoom scale is not fixed
3680 if (!mMinZoomScaleFixed) {
3681 mMinZoomScale = (float) getViewWidth()
Grace Klobae397a882009-08-06 12:04:14 -07003682 / (mDrawHistory ? mHistoryPicture.getWidth()
3683 : mZoomOverviewWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003684 }
3685
3686 // we always force, in case our height changed, in which case we still
3687 // want to send the notification over to webkit
3688 setNewZoomScale(mActualScale, true);
3689 }
3690
3691 @Override
3692 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
3693 super.onScrollChanged(l, t, oldl, oldt);
Patrick Scott0a5ce012009-07-02 08:56:10 -04003694
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003695 sendOurVisibleRect();
3696 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003697
3698
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003699 @Override
3700 public boolean dispatchKeyEvent(KeyEvent event) {
3701 boolean dispatch = true;
3702
3703 if (!inEditingMode()) {
3704 if (event.getAction() == KeyEvent.ACTION_DOWN) {
3705 mGotKeyDown = true;
3706 } else {
3707 if (!mGotKeyDown) {
3708 /*
3709 * We got a key up for which we were not the recipient of
3710 * the original key down. Don't give it to the view.
3711 */
3712 dispatch = false;
3713 }
3714 mGotKeyDown = false;
3715 }
3716 }
3717
3718 if (dispatch) {
3719 return super.dispatchKeyEvent(event);
3720 } else {
3721 // We didn't dispatch, so let something else handle the key
3722 return false;
3723 }
3724 }
3725
3726 // Here are the snap align logic:
3727 // 1. If it starts nearly horizontally or vertically, snap align;
3728 // 2. If there is a dramitic direction change, let it go;
3729 // 3. If there is a same direction back and forth, lock it.
3730
3731 // adjustable parameters
3732 private int mMinLockSnapReverseDistance;
3733 private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
3734 private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
3735
3736 @Override
3737 public boolean onTouchEvent(MotionEvent ev) {
3738 if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
3739 return false;
3740 }
3741
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003742 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003743 Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
3744 + mTouchMode);
3745 }
3746
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003747 int action = ev.getAction();
3748 float x = ev.getX();
3749 float y = ev.getY();
3750 long eventTime = ev.getEventTime();
3751
3752 // Due to the touch screen edge effect, a touch closer to the edge
3753 // always snapped to the edge. As getViewWidth() can be different from
3754 // getWidth() due to the scrollbar, adjusting the point to match
3755 // getViewWidth(). Same applied to the height.
3756 if (x > getViewWidth() - 1) {
3757 x = getViewWidth() - 1;
3758 }
3759 if (y > getViewHeight() - 1) {
3760 y = getViewHeight() - 1;
3761 }
3762
3763 // pass the touch events from UI thread to WebCore thread
3764 if (mForwardTouchEvents && mTouchMode != SCROLL_ZOOM_OUT
3765 && mTouchMode != SCROLL_ZOOM_ANIMATION_IN
3766 && mTouchMode != SCROLL_ZOOM_ANIMATION_OUT
Cary Clarkd6982c92009-05-29 11:02:22 -04003767 && (action != MotionEvent.ACTION_MOVE ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003768 eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
3769 WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
3770 ted.mAction = action;
3771 ted.mX = viewToContent((int) x + mScrollX);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003772 ted.mY = viewToContent((int) y + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003773 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
3774 mLastSentTouchTime = eventTime;
3775 }
3776
3777 int deltaX = (int) (mLastTouchX - x);
3778 int deltaY = (int) (mLastTouchY - y);
3779
3780 switch (action) {
3781 case MotionEvent.ACTION_DOWN: {
3782 if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN
3783 || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
3784 // no interaction while animation is in progress
3785 break;
3786 } else if (mTouchMode == SCROLL_ZOOM_OUT) {
3787 mLastScrollX = mZoomScrollX;
3788 mLastScrollY = mZoomScrollY;
3789 // If two taps are close, ignore the first tap
3790 } else if (!mScroller.isFinished()) {
3791 mScroller.abortAnimation();
3792 mTouchMode = TOUCH_DRAG_START_MODE;
3793 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
3794 } else if (mShiftIsPressed) {
3795 mSelectX = mScrollX + (int) x;
3796 mSelectY = mScrollY + (int) y;
3797 mTouchMode = TOUCH_SELECT_MODE;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003798 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003799 Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
3800 }
3801 nativeMoveSelection(viewToContent(mSelectX)
3802 , viewToContent(mSelectY), false);
3803 mTouchSelection = mExtendSelection = true;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003804 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
3805 mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
3806 if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
3807 mTouchMode = TOUCH_DOUBLE_TAP_MODE;
3808 } else {
3809 // commit the short press action for the previous tap
3810 doShortPress();
3811 // continue, mTouchMode should be still TOUCH_INIT_MODE
3812 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003813 } else {
3814 mTouchMode = TOUCH_INIT_MODE;
3815 mPreventDrag = mForwardTouchEvents;
Cary Clark77d98f42009-07-31 09:40:38 -04003816 mWebViewCore.sendMessage(
3817 EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003818 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
3819 EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
3820 (eventTime - mLastTouchUpTime), eventTime);
3821 }
3822 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003823 // Trigger the link
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003824 if (mTouchMode == TOUCH_INIT_MODE
3825 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003826 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3827 .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
3828 }
3829 // Remember where the motion event started
3830 mLastTouchX = x;
3831 mLastTouchY = y;
3832 mLastTouchTime = eventTime;
3833 mVelocityTracker = VelocityTracker.obtain();
3834 mSnapScrollMode = SNAP_NONE;
3835 break;
3836 }
3837 case MotionEvent.ACTION_MOVE: {
Cary Clarkd6982c92009-05-29 11:02:22 -04003838 if (mTouchMode == TOUCH_DONE_MODE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003839 || mTouchMode == SCROLL_ZOOM_ANIMATION_IN
3840 || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
3841 // no dragging during scroll zoom animation
3842 break;
3843 }
3844 if (mTouchMode == SCROLL_ZOOM_OUT) {
3845 // while fully zoomed out, move the virtual window
3846 moveZoomScrollWindow(x, y);
3847 break;
3848 }
3849 mVelocityTracker.addMovement(ev);
3850
3851 if (mTouchMode != TOUCH_DRAG_MODE) {
3852 if (mTouchMode == TOUCH_SELECT_MODE) {
3853 mSelectX = mScrollX + (int) x;
3854 mSelectY = mScrollY + (int) y;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003855 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003856 Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
3857 }
3858 nativeMoveSelection(viewToContent(mSelectX)
3859 , viewToContent(mSelectY), true);
3860 invalidate();
3861 break;
3862 }
3863 if (mPreventDrag || (deltaX * deltaX + deltaY * deltaY)
3864 < mTouchSlopSquare) {
3865 break;
3866 }
3867
3868 if (mTouchMode == TOUCH_SHORTPRESS_MODE
3869 || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3870 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003871 } else if (mTouchMode == TOUCH_INIT_MODE
3872 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003873 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
3874 }
3875
3876 // if it starts nearly horizontal or vertical, enforce it
3877 int ax = Math.abs(deltaX);
3878 int ay = Math.abs(deltaY);
3879 if (ax > MAX_SLOPE_FOR_DIAG * ay) {
3880 mSnapScrollMode = SNAP_X;
3881 mSnapPositive = deltaX > 0;
3882 } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
3883 mSnapScrollMode = SNAP_Y;
3884 mSnapPositive = deltaY > 0;
3885 }
3886
3887 mTouchMode = TOUCH_DRAG_MODE;
3888 WebViewCore.pauseUpdate(mWebViewCore);
Leon Scroggins72543e12009-07-23 15:29:45 -04003889 if (!mDragFromTextInput) {
3890 nativeHideCursor();
3891 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003892 WebSettings settings = getSettings();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003893 if (settings.supportZoom() && !mInZoomOverview
The Android Open Source Project10592532009-03-18 17:39:46 -07003894 && settings.getBuiltInZoomControls()
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003895 && !mZoomButtonsController.isVisible()
Cary Clarkd6982c92009-05-29 11:02:22 -04003896 && (canZoomScrollOut() ||
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003897 mMinZoomScale < mMaxZoomScale)) {
3898 mZoomButtonsController.setVisible(true);
3899 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003900 }
3901
3902 // do pan
3903 int newScrollX = pinLocX(mScrollX + deltaX);
3904 deltaX = newScrollX - mScrollX;
3905 int newScrollY = pinLocY(mScrollY + deltaY);
3906 deltaY = newScrollY - mScrollY;
3907 boolean done = false;
3908 if (deltaX == 0 && deltaY == 0) {
3909 done = true;
3910 } else {
3911 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
3912 int ax = Math.abs(deltaX);
3913 int ay = Math.abs(deltaY);
3914 if (mSnapScrollMode == SNAP_X) {
3915 // radical change means getting out of snap mode
3916 if (ay > MAX_SLOPE_FOR_DIAG * ax
3917 && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
3918 mSnapScrollMode = SNAP_NONE;
3919 }
3920 // reverse direction means lock in the snap mode
3921 if ((ax > MAX_SLOPE_FOR_DIAG * ay) &&
Cary Clarkd6982c92009-05-29 11:02:22 -04003922 ((mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003923 deltaX < -mMinLockSnapReverseDistance)
3924 || (!mSnapPositive &&
3925 deltaX > mMinLockSnapReverseDistance))) {
3926 mSnapScrollMode = SNAP_X_LOCK;
3927 }
3928 } else {
3929 // radical change means getting out of snap mode
3930 if ((ax > MAX_SLOPE_FOR_DIAG * ay)
3931 && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
3932 mSnapScrollMode = SNAP_NONE;
3933 }
3934 // reverse direction means lock in the snap mode
3935 if ((ay > MAX_SLOPE_FOR_DIAG * ax) &&
Cary Clarkd6982c92009-05-29 11:02:22 -04003936 ((mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003937 deltaY < -mMinLockSnapReverseDistance)
Cary Clarkd6982c92009-05-29 11:02:22 -04003938 || (!mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003939 deltaY > mMinLockSnapReverseDistance))) {
3940 mSnapScrollMode = SNAP_Y_LOCK;
3941 }
3942 }
3943 }
3944
3945 if (mSnapScrollMode == SNAP_X
3946 || mSnapScrollMode == SNAP_X_LOCK) {
3947 scrollBy(deltaX, 0);
3948 mLastTouchX = x;
3949 } else if (mSnapScrollMode == SNAP_Y
3950 || mSnapScrollMode == SNAP_Y_LOCK) {
3951 scrollBy(0, deltaY);
3952 mLastTouchY = y;
3953 } else {
3954 scrollBy(deltaX, deltaY);
3955 mLastTouchX = x;
3956 mLastTouchY = y;
3957 }
3958 mLastTouchTime = eventTime;
3959 mUserScroll = true;
3960 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003961
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003962 if (!getSettings().getBuiltInZoomControls() && !mInZoomOverview) {
The Android Open Source Project10592532009-03-18 17:39:46 -07003963 boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
3964 boolean showMagnify = canZoomScrollOut();
3965 if (mZoomControls != null && (showPlusMinus || showMagnify)) {
3966 if (mZoomControls.getVisibility() == View.VISIBLE) {
3967 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
3968 } else {
3969 mZoomControls.show(showPlusMinus, showMagnify);
3970 }
3971 mPrivateHandler.postDelayed(mZoomControlRunnable,
3972 ZOOM_CONTROLS_TIMEOUT);
3973 }
3974 }
3975
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003976 if (done) {
3977 // return false to indicate that we can't pan out of the
3978 // view space
3979 return false;
3980 }
3981 break;
3982 }
3983 case MotionEvent.ACTION_UP: {
3984 mLastTouchUpTime = eventTime;
3985 switch (mTouchMode) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003986 case TOUCH_DOUBLE_TAP_MODE: // double tap
3987 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad3c6d542009-07-31 14:24:19 -07003988 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003989 doDoubleTap();
3990 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003991 case TOUCH_INIT_MODE: // tap
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003992 if (ENABLE_DOUBLETAP_ZOOM) {
3993 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
3994 if (!mPreventDrag) {
3995 mPrivateHandler.sendMessageDelayed(
3996 mPrivateHandler.obtainMessage(
3997 RELEASE_SINGLE_TAP),
3998 ViewConfiguration.getDoubleTapTimeout());
3999 }
4000 break;
4001 }
4002 // fall through
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004003 case TOUCH_SHORTPRESS_START_MODE:
4004 case TOUCH_SHORTPRESS_MODE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004005 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004006 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
4007 mTouchMode = TOUCH_DONE_MODE;
4008 doShortPress();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004009 break;
4010 case TOUCH_SELECT_MODE:
4011 commitCopy();
4012 mTouchSelection = false;
4013 break;
4014 case SCROLL_ZOOM_ANIMATION_IN:
4015 case SCROLL_ZOOM_ANIMATION_OUT:
4016 // no action during scroll animation
4017 break;
4018 case SCROLL_ZOOM_OUT:
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004019 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004020 Log.v(LOGTAG, "ACTION_UP SCROLL_ZOOM_OUT"
4021 + " eventTime - mLastTouchTime="
4022 + (eventTime - mLastTouchTime));
4023 }
4024 // for now, always zoom back when the drag completes
4025 if (true || eventTime - mLastTouchTime < TAP_TIMEOUT) {
4026 // but if we tap, zoom in where we tap
4027 if (eventTime - mLastTouchTime < TAP_TIMEOUT) {
4028 zoomScrollTap(x, y);
4029 }
4030 // start zooming in back to the original view
4031 setZoomScrollIn();
4032 mTouchMode = SCROLL_ZOOM_ANIMATION_IN;
4033 invalidate();
4034 }
4035 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004036 case TOUCH_DRAG_MODE:
4037 // if the user waits a while w/o moving before the
4038 // up, we don't want to do a fling
4039 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
4040 mVelocityTracker.addMovement(ev);
4041 doFling();
4042 break;
4043 }
4044 WebViewCore.resumeUpdate(mWebViewCore);
4045 break;
4046 case TOUCH_DRAG_START_MODE:
4047 case TOUCH_DONE_MODE:
4048 // do nothing
4049 break;
4050 }
4051 // we also use mVelocityTracker == null to tell us that we are
4052 // not "moving around", so we can take the slower/prettier
4053 // mode in the drawing code
4054 if (mVelocityTracker != null) {
4055 mVelocityTracker.recycle();
4056 mVelocityTracker = null;
4057 }
4058 break;
4059 }
4060 case MotionEvent.ACTION_CANCEL: {
4061 // we also use mVelocityTracker == null to tell us that we are
4062 // not "moving around", so we can take the slower/prettier
4063 // mode in the drawing code
4064 if (mVelocityTracker != null) {
4065 mVelocityTracker.recycle();
4066 mVelocityTracker = null;
4067 }
4068 if (mTouchMode == SCROLL_ZOOM_OUT ||
4069 mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
4070 scrollTo(mZoomScrollX, mZoomScrollY);
4071 } else if (mTouchMode == TOUCH_DRAG_MODE) {
4072 WebViewCore.resumeUpdate(mWebViewCore);
4073 }
4074 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4075 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004076 mTouchMode = TOUCH_DONE_MODE;
Cary Clarke872f3a2009-06-11 09:51:11 -04004077 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004078 break;
4079 }
4080 }
4081 return true;
4082 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004083
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004084 private long mTrackballFirstTime = 0;
4085 private long mTrackballLastTime = 0;
4086 private float mTrackballRemainsX = 0.0f;
4087 private float mTrackballRemainsY = 0.0f;
4088 private int mTrackballXMove = 0;
4089 private int mTrackballYMove = 0;
4090 private boolean mExtendSelection = false;
4091 private boolean mTouchSelection = false;
4092 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
4093 private static final int TRACKBALL_TIMEOUT = 200;
4094 private static final int TRACKBALL_WAIT = 100;
4095 private static final int TRACKBALL_SCALE = 400;
4096 private static final int TRACKBALL_SCROLL_COUNT = 5;
4097 private static final int TRACKBALL_MOVE_COUNT = 10;
4098 private static final int TRACKBALL_MULTIPLIER = 3;
4099 private static final int SELECT_CURSOR_OFFSET = 16;
4100 private int mSelectX = 0;
4101 private int mSelectY = 0;
4102 private boolean mShiftIsPressed = false;
4103 private boolean mTrackballDown = false;
4104 private long mTrackballUpTime = 0;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004105 private long mLastCursorTime = 0;
4106 private Rect mLastCursorBounds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004107
4108 // Set by default; BrowserActivity clears to interpret trackball data
Cary Clarkd6982c92009-05-29 11:02:22 -04004109 // directly for movement. Currently, the framework only passes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004110 // arrow key events, not trackball events, from one child to the next
4111 private boolean mMapTrackballToArrowKeys = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004113 public void setMapTrackballToArrowKeys(boolean setMap) {
4114 mMapTrackballToArrowKeys = setMap;
4115 }
4116
4117 void resetTrackballTime() {
4118 mTrackballLastTime = 0;
4119 }
4120
4121 @Override
4122 public boolean onTrackballEvent(MotionEvent ev) {
4123 long time = ev.getEventTime();
4124 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
4125 if (ev.getY() > 0) pageDown(true);
4126 if (ev.getY() < 0) pageUp(true);
4127 return true;
4128 }
4129 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004130 mPrivateHandler.removeMessages(SWITCH_TO_CLICK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004131 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004132 if (mNativeClass == 0) {
4133 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004134 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004135 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004136 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
4137 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
4138 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004139 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004140 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004141 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004142 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004143 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004144 }
4145 if (isInTouchMode()) requestFocusFromTouch();
4146 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04004147 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004148 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04004149 // LONG_PRESS_CENTER is set in common onKeyDown
4150 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004151 mTrackballDown = false;
4152 mTrackballUpTime = time;
4153 if (mShiftIsPressed) {
4154 if (mExtendSelection) {
4155 commitCopy();
4156 } else {
4157 mExtendSelection = true;
4158 }
4159 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004160 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004161 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004162 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004163 );
4164 }
4165 return false; // let common code in onKeyUp at it
4166 }
4167 if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004168 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004169 return false;
4170 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004171 // no move if we're still waiting on SWITCH_TO_CLICK timeout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004172 if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004173 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004174 return true;
4175 }
4176 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004177 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004178 return true; // discard move if trackball is down
4179 }
4180 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004181 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004182 return true;
4183 }
4184 // TODO: alternatively we can do panning as touch does
4185 switchOutDrawHistory();
4186 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004187 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004188 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004189 + time + " last=" + mTrackballLastTime);
4190 }
4191 mTrackballFirstTime = time;
4192 mTrackballXMove = mTrackballYMove = 0;
4193 }
4194 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004195 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004196 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
4197 }
4198 mTrackballRemainsX += ev.getX();
4199 mTrackballRemainsY += ev.getY();
4200 doTrackball(time);
4201 return true;
4202 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004204 void moveSelection(float xRate, float yRate) {
4205 if (mNativeClass == 0)
4206 return;
4207 int width = getViewWidth();
4208 int height = getViewHeight();
4209 mSelectX += scaleTrackballX(xRate, width);
4210 mSelectY += scaleTrackballY(yRate, height);
4211 int maxX = width + mScrollX;
4212 int maxY = height + mScrollY;
4213 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
4214 , mSelectX));
4215 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
4216 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004217 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004218 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004219 + " mSelectX=" + mSelectX
4220 + " mSelectY=" + mSelectY
4221 + " mScrollX=" + mScrollX
4222 + " mScrollY=" + mScrollY
4223 + " xRate=" + xRate
4224 + " yRate=" + yRate
4225 );
4226 }
4227 nativeMoveSelection(viewToContent(mSelectX)
4228 , viewToContent(mSelectY), mExtendSelection);
4229 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004230 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004231 : 0;
4232 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004233 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004234 : 0;
4235 pinScrollBy(scrollX, scrollY, true, 0);
4236 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
4237 requestRectangleOnScreen(select);
4238 invalidate();
4239 }
4240
4241 private int scaleTrackballX(float xRate, int width) {
4242 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
4243 int nextXMove = xMove;
4244 if (xMove > 0) {
4245 if (xMove > mTrackballXMove) {
4246 xMove -= mTrackballXMove;
4247 }
4248 } else if (xMove < mTrackballXMove) {
4249 xMove -= mTrackballXMove;
4250 }
4251 mTrackballXMove = nextXMove;
4252 return xMove;
4253 }
4254
4255 private int scaleTrackballY(float yRate, int height) {
4256 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
4257 int nextYMove = yMove;
4258 if (yMove > 0) {
4259 if (yMove > mTrackballYMove) {
4260 yMove -= mTrackballYMove;
4261 }
4262 } else if (yMove < mTrackballYMove) {
4263 yMove -= mTrackballYMove;
4264 }
4265 mTrackballYMove = nextYMove;
4266 return yMove;
4267 }
4268
4269 private int keyCodeToSoundsEffect(int keyCode) {
4270 switch(keyCode) {
4271 case KeyEvent.KEYCODE_DPAD_UP:
4272 return SoundEffectConstants.NAVIGATION_UP;
4273 case KeyEvent.KEYCODE_DPAD_RIGHT:
4274 return SoundEffectConstants.NAVIGATION_RIGHT;
4275 case KeyEvent.KEYCODE_DPAD_DOWN:
4276 return SoundEffectConstants.NAVIGATION_DOWN;
4277 case KeyEvent.KEYCODE_DPAD_LEFT:
4278 return SoundEffectConstants.NAVIGATION_LEFT;
4279 }
4280 throw new IllegalArgumentException("keyCode must be one of " +
4281 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
4282 "KEYCODE_DPAD_LEFT}.");
4283 }
4284
4285 private void doTrackball(long time) {
4286 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
4287 if (elapsed == 0) {
4288 elapsed = TRACKBALL_TIMEOUT;
4289 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004290 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004291 float yRate = mTrackballRemainsY * 1000 / elapsed;
4292 if (mShiftIsPressed) {
4293 moveSelection(xRate, yRate);
4294 mTrackballRemainsX = mTrackballRemainsY = 0;
4295 return;
4296 }
4297 float ax = Math.abs(xRate);
4298 float ay = Math.abs(yRate);
4299 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004300 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004301 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
4302 + " xRate=" + xRate
4303 + " yRate=" + yRate
4304 + " mTrackballRemainsX=" + mTrackballRemainsX
4305 + " mTrackballRemainsY=" + mTrackballRemainsY);
4306 }
4307 int width = mContentWidth - getViewWidth();
4308 int height = mContentHeight - getViewHeight();
4309 if (width < 0) width = 0;
4310 if (height < 0) height = 0;
4311 if (mTouchMode == SCROLL_ZOOM_OUT) {
4312 int oldX = mZoomScrollX;
4313 int oldY = mZoomScrollY;
4314 int maxWH = Math.max(width, height);
4315 mZoomScrollX += scaleTrackballX(xRate, maxWH);
4316 mZoomScrollY += scaleTrackballY(yRate, maxWH);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004317 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004318 Log.v(LOGTAG, "doTrackball SCROLL_ZOOM_OUT"
Cary Clarkd6982c92009-05-29 11:02:22 -04004319 + " mZoomScrollX=" + mZoomScrollX
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004320 + " mZoomScrollY=" + mZoomScrollY);
4321 }
4322 mZoomScrollX = Math.min(width, Math.max(0, mZoomScrollX));
4323 mZoomScrollY = Math.min(height, Math.max(0, mZoomScrollY));
4324 if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
4325 invalidate();
4326 }
4327 mTrackballRemainsX = mTrackballRemainsY = 0;
4328 return;
4329 }
4330 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
4331 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
4332 maxA = Math.max(ax, ay);
4333 int count = Math.max(0, (int) maxA);
4334 int oldScrollX = mScrollX;
4335 int oldScrollY = mScrollY;
4336 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004337 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
4338 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004339 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
4340 KeyEvent.KEYCODE_DPAD_RIGHT;
4341 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004342 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004343 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004344 + " count=" + count
4345 + " mTrackballRemainsX=" + mTrackballRemainsX
4346 + " mTrackballRemainsY=" + mTrackballRemainsY);
4347 }
Cary Clark215b72c2009-06-26 14:38:43 -04004348 if (navHandledKey(selectKeyCode, count, false, time, false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004349 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
4350 }
4351 mTrackballRemainsX = mTrackballRemainsY = 0;
4352 }
4353 if (count >= TRACKBALL_SCROLL_COUNT) {
4354 int xMove = scaleTrackballX(xRate, width);
4355 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004356 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004357 Log.v(LOGTAG, "doTrackball pinScrollBy"
4358 + " count=" + count
4359 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04004360 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
4361 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004362 );
4363 }
4364 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
4365 xMove = 0;
4366 }
4367 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
4368 yMove = 0;
4369 }
4370 if (xMove != 0 || yMove != 0) {
4371 pinScrollBy(xMove, yMove, true, 0);
4372 }
4373 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004374 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004375 }
4376
4377 public void flingScroll(int vx, int vy) {
4378 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
4379 int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
Cary Clarkd6982c92009-05-29 11:02:22 -04004380
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004381 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
4382 invalidate();
4383 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004384
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004385 private void doFling() {
4386 if (mVelocityTracker == null) {
4387 return;
4388 }
4389 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
4390 int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
4391
Romain Guy4296fc42009-07-06 11:48:52 -07004392 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004393 int vx = (int) mVelocityTracker.getXVelocity();
4394 int vy = (int) mVelocityTracker.getYVelocity();
4395
4396 if (mSnapScrollMode != SNAP_NONE) {
4397 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_X_LOCK) {
4398 vy = 0;
4399 } else {
4400 vx = 0;
4401 }
4402 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004403
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004404 if (true /* EMG release: make our fling more like Maps' */) {
4405 // maps cuts their velocity in half
4406 vx = vx * 3 / 4;
4407 vy = vy * 3 / 4;
4408 }
4409
4410 mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
4411 // TODO: duration is calculated based on velocity, if the range is
4412 // small, the animation will stop before duration is up. We may
4413 // want to calculate how long the animation is going to run to precisely
4414 // resume the webcore update.
4415 final int time = mScroller.getDuration();
4416 mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_UPDATE, time);
4417 invalidate();
4418 }
4419
4420 private boolean zoomWithPreview(float scale) {
4421 float oldScale = mActualScale;
Grace Kloba675c7d22009-07-23 09:21:21 -07004422 mInitialScrollX = mScrollX;
4423 mInitialScrollY = mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004424
Grace Kloba25737912009-06-19 12:42:47 -07004425 // snap to DEFAULT_SCALE if it is close
Grace Kloba0d8b77c2009-06-25 11:20:51 -07004426 if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
4427 scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004428 }
4429
4430 setNewZoomScale(scale, false);
4431
4432 if (oldScale != mActualScale) {
4433 // use mZoomPickerScale to see zoom preview first
4434 mZoomStart = SystemClock.uptimeMillis();
4435 mInvInitialZoomScale = 1.0f / oldScale;
4436 mInvFinalZoomScale = 1.0f / mActualScale;
4437 mZoomScale = mActualScale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004438 if (!mInZoomOverview) {
4439 mLastScale = scale;
4440 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004441 invalidate();
4442 return true;
4443 } else {
4444 return false;
4445 }
4446 }
4447
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004448 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004449 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
4450 * in charge of installing this view to the view hierarchy. This view will
4451 * become visible when the user starts scrolling via touch and fade away if
4452 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004453 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07004454 * API version 3 introduces a built-in zoom mechanism that is shown
4455 * automatically by the MapView. This is the preferred approach for
4456 * showing the zoom UI.
4457 *
4458 * @deprecated The built-in zoom mechanism is preferred, see
4459 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004460 */
The Android Open Source Project10592532009-03-18 17:39:46 -07004461 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004462 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07004463 if (!getSettings().supportZoom()) {
4464 Log.w(LOGTAG, "This WebView doesn't support zoom.");
4465 return null;
4466 }
4467 if (mZoomControls == null) {
4468 mZoomControls = createZoomControls();
Cary Clarkd6982c92009-05-29 11:02:22 -04004469
The Android Open Source Project10592532009-03-18 17:39:46 -07004470 /*
4471 * need to be set to VISIBLE first so that getMeasuredHeight() in
4472 * {@link #onSizeChanged()} can return the measured value for proper
4473 * layout.
4474 */
4475 mZoomControls.setVisibility(View.VISIBLE);
4476 mZoomControlRunnable = new Runnable() {
4477 public void run() {
Cary Clarkd6982c92009-05-29 11:02:22 -04004478
The Android Open Source Project10592532009-03-18 17:39:46 -07004479 /* Don't dismiss the controls if the user has
4480 * focus on them. Wait and check again later.
4481 */
4482 if (!mZoomControls.hasFocus()) {
4483 mZoomControls.hide();
4484 } else {
4485 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4486 mPrivateHandler.postDelayed(mZoomControlRunnable,
4487 ZOOM_CONTROLS_TIMEOUT);
4488 }
4489 }
4490 };
4491 }
4492 return mZoomControls;
4493 }
4494
4495 private ExtendedZoomControls createZoomControls() {
4496 ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
4497 , null);
4498 zoomControls.setOnZoomInClickListener(new OnClickListener() {
4499 public void onClick(View v) {
4500 // reset time out
4501 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4502 mPrivateHandler.postDelayed(mZoomControlRunnable,
4503 ZOOM_CONTROLS_TIMEOUT);
4504 zoomIn();
4505 }
4506 });
4507 zoomControls.setOnZoomOutClickListener(new OnClickListener() {
4508 public void onClick(View v) {
4509 // reset time out
4510 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4511 mPrivateHandler.postDelayed(mZoomControlRunnable,
4512 ZOOM_CONTROLS_TIMEOUT);
4513 zoomOut();
4514 }
4515 });
4516 zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() {
4517 public void onClick(View v) {
4518 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4519 mPrivateHandler.postDelayed(mZoomControlRunnable,
4520 ZOOM_CONTROLS_TIMEOUT);
4521 zoomScrollOut();
4522 }
4523 });
4524 return zoomControls;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004525 }
4526
4527 /**
4528 * Gets the {@link ZoomButtonsController} which can be used to add
4529 * additional buttons to the zoom controls window.
Cary Clarkd6982c92009-05-29 11:02:22 -04004530 *
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004531 * @return The instance of {@link ZoomButtonsController} used by this class,
4532 * or null if it is unavailable.
The Android Open Source Project10592532009-03-18 17:39:46 -07004533 * @hide
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004534 */
4535 public ZoomButtonsController getZoomButtonsController() {
4536 return mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004537 }
4538
4539 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004540 * Perform zoom in in the webview
4541 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
4542 */
4543 public boolean zoomIn() {
4544 // TODO: alternatively we can disallow this during draw history mode
4545 switchOutDrawHistory();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004546 // Center zooming to the center of the screen.
4547 mZoomCenterX = getViewWidth() * .5f;
4548 mZoomCenterY = getViewHeight() * .5f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004549 return zoomWithPreview(mActualScale * 1.25f);
4550 }
4551
4552 /**
4553 * Perform zoom out in the webview
4554 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
4555 */
4556 public boolean zoomOut() {
4557 // TODO: alternatively we can disallow this during draw history mode
4558 switchOutDrawHistory();
Grace Klobae397a882009-08-06 12:04:14 -07004559 float scale = mActualScale * 0.8f;
4560 if (scale < (mMinZoomScale + 0.1f) && WebView.ENABLE_DOUBLETAP_ZOOM
4561 && mWebViewCore.getSettings().getUseWideViewPort()) {
4562 // when zoom out to min scale, switch to overview mode
4563 doDoubleTap();
4564 return true;
4565 } else {
4566 // Center zooming to the center of the screen.
4567 mZoomCenterX = getViewWidth() * .5f;
4568 mZoomCenterY = getViewHeight() * .5f;
4569 return zoomWithPreview(scale);
4570 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004571 }
4572
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004573 private void updateSelection() {
4574 if (mNativeClass == 0) {
4575 return;
4576 }
4577 // mLastTouchX and mLastTouchY are the point in the current viewport
4578 int contentX = viewToContent((int) mLastTouchX + mScrollX);
4579 int contentY = viewToContent((int) mLastTouchY + mScrollY);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004580 Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
4581 contentX + mNavSlop, contentY + mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004582 nativeSelectBestAt(rect);
4583 }
4584
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004585 /**
Leon Scroggins72543e12009-07-23 15:29:45 -04004586 * Scroll the focused text field/area to match the WebTextView
4587 * @param x New x position of the WebTextView in view coordinates
4588 * @param y New y position of the WebTextView in view coordinates
4589 */
4590 /*package*/ void scrollFocusedTextInput(int x, int y) {
4591 if (!inEditingMode() || mWebViewCore == null) {
4592 return;
4593 }
4594 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, viewToContent(x),
4595 viewToContent(y));
4596 }
4597
4598 /**
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004599 * Set our starting point and time for a drag from the WebTextView.
4600 */
4601 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
4602 if (!inEditingMode()) {
4603 return;
4604 }
4605 mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
4606 mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
4607 mLastTouchTime = eventTime;
4608 if (!mScroller.isFinished()) {
4609 mScroller.abortAnimation();
4610 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
4611 }
4612 mSnapScrollMode = SNAP_NONE;
4613 mVelocityTracker = VelocityTracker.obtain();
4614 mTouchMode = TOUCH_DRAG_START_MODE;
4615 }
4616
4617 /**
4618 * Given a motion event from the WebTextView, set its location to our
4619 * coordinates, and handle the event.
4620 */
4621 /*package*/ boolean textFieldDrag(MotionEvent event) {
4622 if (!inEditingMode()) {
4623 return false;
4624 }
Leon Scroggins72543e12009-07-23 15:29:45 -04004625 mDragFromTextInput = true;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004626 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
4627 (float) (mWebTextView.getTop() - mScrollY));
Leon Scroggins72543e12009-07-23 15:29:45 -04004628 boolean result = onTouchEvent(event);
4629 mDragFromTextInput = false;
4630 return result;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004631 }
4632
Leon Scroggins6679f2f2009-08-12 18:48:10 -04004633 /**
4634 * Do a touch up from a WebTextView. This will be handled by webkit to
4635 * change the selection.
4636 * @param event MotionEvent in the WebTextView's coordinates.
4637 */
4638 /*package*/ void touchUpOnTextField(MotionEvent event) {
4639 if (!inEditingMode()) {
4640 return;
4641 }
4642 int x = viewToContent((int) event.getX() + mWebTextView.getLeft());
4643 int y = viewToContent((int) event.getY() + mWebTextView.getTop());
4644 nativeTextInputMotionUp(x, y);
4645 }
4646
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004647 /*package*/ void shortPressOnTextField() {
4648 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004649 View v = mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004650 int x = viewToContent((v.getLeft() + v.getRight()) >> 1);
4651 int y = viewToContent((v.getTop() + v.getBottom()) >> 1);
Leon Scroggins6679f2f2009-08-12 18:48:10 -04004652 nativeTextInputMotionUp(x, y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004653 }
4654 }
4655
4656 private void doShortPress() {
4657 if (mNativeClass == 0) {
4658 return;
4659 }
4660 switchOutDrawHistory();
4661 // mLastTouchX and mLastTouchY are the point in the current viewport
4662 int contentX = viewToContent((int) mLastTouchX + mScrollX);
4663 int contentY = viewToContent((int) mLastTouchY + mScrollY);
Cary Clark8f9ff7e2009-05-14 10:09:26 -04004664 if (nativeMotionUp(contentX, contentY, mNavSlop)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004665 if (mLogEvent) {
4666 Checkin.updateStats(mContext.getContentResolver(),
4667 Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
4668 }
4669 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004670 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004671 playSoundEffect(SoundEffectConstants.CLICK);
4672 }
4673 }
4674
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004675 private void doDoubleTap() {
4676 if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
4677 return;
4678 }
4679 mZoomCenterX = mLastTouchX;
4680 mZoomCenterY = mLastTouchY;
4681 mInZoomOverview = !mInZoomOverview;
4682 if (mInZoomOverview) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004683 if (getSettings().getBuiltInZoomControls()) {
4684 if (mZoomButtonsController.isVisible()) {
4685 mZoomButtonsController.setVisible(false);
4686 }
4687 } else {
4688 if (mZoomControlRunnable != null) {
4689 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4690 }
4691 if (mZoomControls != null) {
4692 mZoomControls.hide();
4693 }
4694 }
Grace Klobae397a882009-08-06 12:04:14 -07004695 zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004696 } else {
4697 // mLastTouchX and mLastTouchY are the point in the current viewport
4698 int contentX = viewToContent((int) mLastTouchX + mScrollX);
4699 int contentY = viewToContent((int) mLastTouchY + mScrollY);
Cary Clark77d98f42009-07-31 09:40:38 -04004700 int left = nativeGetBlockLeftEdge(contentX, contentY, mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004701 if (left != NO_LEFTEDGE) {
4702 // add a 5pt padding to the left edge. Re-calculate the zoom
4703 // center so that the new scroll x will be on the left edge.
4704 mZoomCenterX = left < 5 ? 0 : (left - 5) * mLastScale
4705 * mActualScale / (mLastScale - mActualScale);
4706 }
4707 zoomWithPreview(mLastScale);
4708 }
4709 }
4710
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004711 // Called by JNI to handle a touch on a node representing an email address,
4712 // address, or phone number
4713 private void overrideLoading(String url) {
4714 mCallbackProxy.uiOverrideUrlLoading(url);
4715 }
4716
Derek Sollenberger945f3b42009-07-02 10:05:21 -04004717 // called by JNI
4718 private void sendPluginState(int state) {
4719 WebViewCore.PluginStateData psd = new WebViewCore.PluginStateData();
4720 psd.mFrame = nativeCursorFramePointer();
4721 psd.mNode = nativeCursorNodePointer();
4722 psd.mState = state;
4723 mWebViewCore.sendMessage(EventHub.PLUGIN_STATE, psd);
4724 }
4725
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004726 @Override
4727 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
4728 boolean result = false;
4729 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004730 result = mWebTextView.requestFocus(direction,
4731 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004732 } else {
4733 result = super.requestFocus(direction, previouslyFocusedRect);
4734 if (mWebViewCore.getSettings().getNeedInitialFocus()) {
4735 // For cases such as GMail, where we gain focus from a direction,
4736 // we want to move to the first available link.
4737 // FIXME: If there are no visible links, we may not want to
4738 int fakeKeyDirection = 0;
4739 switch(direction) {
4740 case View.FOCUS_UP:
4741 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
4742 break;
4743 case View.FOCUS_DOWN:
4744 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
4745 break;
4746 case View.FOCUS_LEFT:
4747 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
4748 break;
4749 case View.FOCUS_RIGHT:
4750 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
4751 break;
4752 default:
4753 return result;
4754 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004755 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Cary Clark215b72c2009-06-26 14:38:43 -04004756 navHandledKey(fakeKeyDirection, 1, true, 0, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004757 }
4758 }
4759 }
4760 return result;
4761 }
4762
4763 @Override
4764 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
4765 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4766
4767 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
4768 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
4769 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
4770 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
4771
4772 int measuredHeight = heightSize;
4773 int measuredWidth = widthSize;
4774
4775 // Grab the content size from WebViewCore.
4776 int contentHeight = mContentHeight;
4777 int contentWidth = mContentWidth;
4778
4779// Log.d(LOGTAG, "------- measure " + heightMode);
4780
4781 if (heightMode != MeasureSpec.EXACTLY) {
4782 mHeightCanMeasure = true;
4783 measuredHeight = contentHeight;
4784 if (heightMode == MeasureSpec.AT_MOST) {
4785 // If we are larger than the AT_MOST height, then our height can
4786 // no longer be measured and we should scroll internally.
4787 if (measuredHeight > heightSize) {
4788 measuredHeight = heightSize;
4789 mHeightCanMeasure = false;
4790 }
4791 }
4792 } else {
4793 mHeightCanMeasure = false;
4794 }
4795 if (mNativeClass != 0) {
4796 nativeSetHeightCanMeasure(mHeightCanMeasure);
4797 }
4798 // For the width, always use the given size unless unspecified.
4799 if (widthMode == MeasureSpec.UNSPECIFIED) {
4800 mWidthCanMeasure = true;
4801 measuredWidth = contentWidth;
4802 } else {
4803 mWidthCanMeasure = false;
4804 }
4805
4806 synchronized (this) {
4807 setMeasuredDimension(measuredWidth, measuredHeight);
4808 }
4809 }
4810
4811 @Override
4812 public boolean requestChildRectangleOnScreen(View child,
4813 Rect rect,
4814 boolean immediate) {
4815 rect.offset(child.getLeft() - child.getScrollX(),
4816 child.getTop() - child.getScrollY());
4817
4818 int height = getHeight() - getHorizontalScrollbarHeight();
4819 int screenTop = mScrollY;
4820 int screenBottom = screenTop + height;
4821
4822 int scrollYDelta = 0;
4823
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004824 if (rect.bottom > screenBottom) {
4825 int oneThirdOfScreenHeight = height / 3;
4826 if (rect.height() > 2 * oneThirdOfScreenHeight) {
4827 // If the rectangle is too tall to fit in the bottom two thirds
4828 // of the screen, place it at the top.
4829 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004830 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004831 // If the rectangle will still fit on screen, we want its
4832 // top to be in the top third of the screen.
4833 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004834 }
4835 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004836 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004837 }
4838
4839 int width = getWidth() - getVerticalScrollbarWidth();
4840 int screenLeft = mScrollX;
4841 int screenRight = screenLeft + width;
4842
4843 int scrollXDelta = 0;
4844
4845 if (rect.right > screenRight && rect.left > screenLeft) {
4846 if (rect.width() > width) {
4847 scrollXDelta += (rect.left - screenLeft);
4848 } else {
4849 scrollXDelta += (rect.right - screenRight);
4850 }
4851 } else if (rect.left < screenLeft) {
4852 scrollXDelta -= (screenLeft - rect.left);
4853 }
4854
4855 if ((scrollYDelta | scrollXDelta) != 0) {
4856 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
4857 }
4858
4859 return false;
4860 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004861
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004862 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
4863 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04004864 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
4865 arg.mReplace = replace;
4866 arg.mNewStart = newStart;
4867 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004868 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04004869 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004870 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
4871 }
4872
4873 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkf958fcc2009-06-12 17:33:37 -04004874 if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) {
4875 mWebViewCore.sendMessage(EventHub.CLICK);
4876 }
Cary Clarkded054c2009-06-15 10:26:08 -04004877 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
4878 arg.mEvent = event;
4879 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004880 // Increase our text generation number, and pass it to webcore thread
4881 mTextGeneration++;
4882 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
4883 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04004884 // To make sure the host application, like Browser, has the up to date
4885 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004886 // document state.
4887 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
4888 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04004889 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004890 }
4891
4892 /* package */ WebViewCore getWebViewCore() {
4893 return mWebViewCore;
4894 }
4895
4896 //-------------------------------------------------------------------------
4897 // Methods can be called from a separate thread, like WebViewCore
4898 // If it needs to call the View system, it has to send message.
4899 //-------------------------------------------------------------------------
4900
4901 /**
4902 * General handler to receive message coming from webkit thread
4903 */
4904 class PrivateHandler extends Handler {
4905 @Override
4906 public void handleMessage(Message msg) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004907 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004908 Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
4909 > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004910 : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
4911 }
4912 switch (msg.what) {
4913 case REMEMBER_PASSWORD: {
4914 mDatabase.setUsernamePassword(
4915 msg.getData().getString("host"),
4916 msg.getData().getString("username"),
4917 msg.getData().getString("password"));
4918 ((Message) msg.obj).sendToTarget();
4919 break;
4920 }
4921 case NEVER_REMEMBER_PASSWORD: {
4922 mDatabase.setUsernamePassword(
4923 msg.getData().getString("host"), null, null);
4924 ((Message) msg.obj).sendToTarget();
4925 break;
4926 }
4927 case SWITCH_TO_SHORTPRESS: {
4928 if (mTouchMode == TOUCH_INIT_MODE) {
4929 mTouchMode = TOUCH_SHORTPRESS_START_MODE;
4930 updateSelection();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004931 } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
4932 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004933 }
4934 break;
4935 }
4936 case SWITCH_TO_LONGPRESS: {
Grace Kloba6c451b72009-06-25 12:25:30 -07004937 if (!mPreventDrag) {
4938 mTouchMode = TOUCH_DONE_MODE;
4939 performLongClick();
Grace Kloba959046c2009-06-25 14:28:04 -07004940 rebuildWebTextView();
Grace Kloba6c451b72009-06-25 12:25:30 -07004941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004942 break;
4943 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004944 case RELEASE_SINGLE_TAP: {
4945 if (!mPreventDrag) {
4946 mTouchMode = TOUCH_DONE_MODE;
4947 doShortPress();
4948 }
4949 break;
4950 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004951 case SWITCH_TO_CLICK:
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004952 // The user clicked with the trackball, and did not click a
4953 // second time, so perform the action of a trackball single
4954 // click
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004955 mTouchMode = TOUCH_DONE_MODE;
Cary Clarkd6982c92009-05-29 11:02:22 -04004956 Rect visibleRect = sendOurVisibleRect();
4957 // Note that sendOurVisibleRect calls viewToContent, so the
4958 // coordinates should be in content coordinates.
4959 if (!nativeCursorIntersects(visibleRect)) {
4960 break;
4961 }
4962 nativeSetFollowedLink(true);
Cary Clark215b72c2009-06-26 14:38:43 -04004963 nativeUpdatePluginReceivesEvents();
Leon Scrogginsef6da8f2009-06-26 14:00:40 -04004964 WebViewCore.CursorData data = cursorData();
4965 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
Cary Clarkd6982c92009-05-29 11:02:22 -04004966 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004967 boolean isTextInput = nativeCursorIsTextInput();
4968 if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading(
4969 nativeCursorText())) {
Leon Scrogginsef6da8f2009-06-26 14:00:40 -04004970 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
4971 nativeCursorNodePointer());
Cary Clarkd6982c92009-05-29 11:02:22 -04004972 }
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004973 if (isTextInput) {
4974 rebuildWebTextView();
4975 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004976 break;
4977 case SCROLL_BY_MSG_ID:
4978 setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
4979 break;
4980 case SYNC_SCROLL_TO_MSG_ID:
4981 if (mUserScroll) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004982 // if user has scrolled explicitly, don't sync the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004983 // scroll position any more
4984 mUserScroll = false;
4985 break;
4986 }
4987 // fall through
4988 case SCROLL_TO_MSG_ID:
4989 if (setContentScrollTo(msg.arg1, msg.arg2)) {
4990 // if we can't scroll to the exact position due to pin,
Cary Clarkd6982c92009-05-29 11:02:22 -04004991 // send a message to WebCore to re-scroll when we get a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004992 // new picture
4993 mUserScroll = false;
4994 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
4995 msg.arg1, msg.arg2);
4996 }
4997 break;
4998 case SPAWN_SCROLL_TO_MSG_ID:
4999 spawnContentScrollTo(msg.arg1, msg.arg2);
5000 break;
Grace Klobae397a882009-08-06 12:04:14 -07005001 case NEW_PICTURE_MSG_ID: {
5002 WebSettings settings = mWebViewCore.getSettings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005003 // called for new content
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005004 final int viewWidth = getViewWidth();
Cary Clarkd6982c92009-05-29 11:02:22 -04005005 final WebViewCore.DrawData draw =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005006 (WebViewCore.DrawData) msg.obj;
5007 final Point viewSize = draw.mViewPoint;
Grace Klobae397a882009-08-06 12:04:14 -07005008 boolean useWideViewport = settings.getUseWideViewPort();
Grace Klobaef347ef2009-07-30 11:20:32 -07005009 WebViewCore.RestoreState restoreState = draw.mRestoreState;
5010 if (restoreState != null) {
5011 mInZoomOverview = false;
5012 mLastScale = restoreState.mTextWrapScale;
5013 if (restoreState.mMinScale == 0) {
5014 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
5015 mMinZoomScaleFixed = false;
5016 } else {
5017 mMinZoomScale = restoreState.mMinScale;
5018 mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005019 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005020 if (restoreState.mMaxScale == 0) {
5021 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
5022 } else {
5023 mMaxZoomScale = restoreState.mMaxScale;
5024 }
5025 if (useWideViewport && restoreState.mViewScale == 0) {
Grace Klobae397a882009-08-06 12:04:14 -07005026 mInZoomOverview = ENABLE_DOUBLETAP_ZOOM
5027 && settings.getLoadWithOverviewMode();
Grace Klobaef347ef2009-07-30 11:20:32 -07005028 }
5029 setNewZoomScale(mLastScale, false);
5030 setContentScrollTo(restoreState.mScrollX,
5031 restoreState.mScrollY);
5032 // As we are on a new page, remove the WebTextView. This
5033 // is necessary for page loads driven by webkit, and in
5034 // particular when the user was on a password field, so
5035 // the WebTextView was visible.
5036 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005037 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005038 // We update the layout (i.e. request a layout from the
5039 // view system) if the last view size that we sent to
5040 // WebCore matches the view size of the picture we just
5041 // received in the fixed dimension.
5042 final boolean updateLayout = viewSize.x == mLastWidthSent
5043 && viewSize.y == mLastHeightSent;
Cary Clarkd6982c92009-05-29 11:02:22 -04005044 recordNewContentSize(draw.mWidthHeight.x,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005045 draw.mWidthHeight.y, updateLayout);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005046 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005047 Rect b = draw.mInvalRegion.getBounds();
5048 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
5049 b.left+","+b.top+","+b.right+","+b.bottom+"}");
5050 }
5051 invalidate(contentToView(draw.mInvalRegion.getBounds()));
5052 if (mPictureListener != null) {
5053 mPictureListener.onNewPicture(WebView.this, capturePicture());
5054 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005055 if (useWideViewport) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005056 mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
5057 draw.mViewPoint.x);
5058 }
5059 if (!mMinZoomScaleFixed) {
Grace Klobae397a882009-08-06 12:04:14 -07005060 mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005061 }
5062 if (!mDrawHistory && mInZoomOverview) {
5063 // fit the content width to the current view. Ignore
5064 // the rounding error case.
5065 if (Math.abs((viewWidth * mInvActualScale)
5066 - mZoomOverviewWidth) > 1) {
Grace Klobae397a882009-08-06 12:04:14 -07005067 setNewZoomScale((float) viewWidth
5068 / mZoomOverviewWidth, false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005069 }
5070 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005071 break;
Grace Klobae397a882009-08-06 12:04:14 -07005072 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005073 case WEBCORE_INITIALIZED_MSG_ID:
5074 // nativeCreate sets mNativeClass to a non-zero value
5075 nativeCreate(msg.arg1);
5076 break;
5077 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
5078 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04005079 // and representing the same node as the pointer.
5080 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005081 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005082 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005083 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005084 int start = Selection.getSelectionStart(text);
5085 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005086 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005087 // Restore the selection, which may have been
5088 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005089 Spannable pword =
5090 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005091 Selection.setSelection(pword, start, end);
5092 // If the text entry has created more events, ignore
5093 // this one.
5094 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005095 mWebTextView.setTextAndKeepSelection(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005096 (String) msg.obj);
5097 }
5098 }
5099 break;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005100 case UPDATE_TEXT_SELECTION_MSG_ID:
5101 if (inEditingMode()
5102 && mWebTextView.isSameTextField(msg.arg1)
5103 && msg.arg2 == mTextGeneration) {
5104 WebViewCore.TextSelectionData tData
5105 = (WebViewCore.TextSelectionData) msg.obj;
5106 mWebTextView.setSelectionFromWebKit(tData.mStart,
5107 tData.mEnd);
5108 }
5109 break;
Cary Clark215b72c2009-06-26 14:38:43 -04005110 case MOVE_OUT_OF_PLUGIN:
5111 if (nativePluginEatsNavKey()) {
5112 navHandledKey(msg.arg1, 1, false, 0, true);
5113 }
5114 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005115 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04005116 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005117 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04005118 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005119 mWebTextView.bringIntoView();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005120 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005121 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005122 break;
Cary Clark243ea062009-06-25 10:49:32 -04005123 case CLEAR_TEXT_ENTRY:
5124 clearTextEntry();
5125 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005126 case INVAL_RECT_MSG_ID: {
5127 Rect r = (Rect)msg.obj;
5128 if (r == null) {
5129 invalidate();
5130 } else {
5131 // we need to scale r from content into view coords,
5132 // which viewInvalidate() does for us
5133 viewInvalidate(r.left, r.top, r.right, r.bottom);
5134 }
5135 break;
5136 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005137 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04005138 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005139 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005140 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005141 }
5142 break;
5143 case UPDATE_CLIPBOARD:
5144 String str = (String) msg.obj;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005145 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005146 Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
5147 }
5148 try {
5149 IClipboard clip = IClipboard.Stub.asInterface(
5150 ServiceManager.getService("clipboard"));
5151 clip.setClipboardText(str);
5152 } catch (android.os.RemoteException e) {
5153 Log.e(LOGTAG, "Clipboard failed", e);
5154 }
5155 break;
5156 case RESUME_WEBCORE_UPDATE:
5157 WebViewCore.resumeUpdate(mWebViewCore);
5158 break;
5159
Leon Scrogginse3225672009-06-03 15:53:13 -04005160 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005161 // as this is shared by keydown and trackballdown, reset all
5162 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04005163 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005164 mTrackballDown = false;
Leon Scrogginse3225672009-06-03 15:53:13 -04005165 // LONG_PRESS_CENTER is sent as a delayed message. If we
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005166 // switch to windows overview, the WebView will be
5167 // temporarily removed from the view system. In that case,
5168 // do nothing.
5169 if (getParent() != null) {
5170 performLongClick();
5171 }
5172 break;
5173
5174 case WEBCORE_NEED_TOUCH_EVENTS:
5175 mForwardTouchEvents = (msg.arg1 != 0);
5176 break;
5177
5178 case PREVENT_TOUCH_ID:
5179 if (msg.arg1 == MotionEvent.ACTION_DOWN) {
5180 mPreventDrag = msg.arg2 == 1;
5181 if (mPreventDrag) {
5182 mTouchMode = TOUCH_DONE_MODE;
5183 }
5184 }
5185 break;
5186
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005187 case REQUEST_KEYBOARD:
5188 if (msg.arg1 == 0) {
5189 hideSoftKeyboard();
5190 } else {
5191 displaySoftKeyboard(false);
5192 }
5193 break;
5194
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005195 default:
5196 super.handleMessage(msg);
5197 break;
5198 }
5199 }
5200 }
5201
5202 // Class used to use a dropdown for a <select> element
5203 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005204 // Whether the listbox allows multiple selection.
5205 private boolean mMultiple;
5206 // Passed in to a list with multiple selection to tell
5207 // which items are selected.
5208 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04005209 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005210 // where the initial selection is.
5211 private int mSelection;
5212
5213 private Container[] mContainers;
5214
5215 // Need these to provide stable ids to my ArrayAdapter,
5216 // which normally does not have stable ids. (Bug 1250098)
5217 private class Container extends Object {
5218 String mString;
5219 boolean mEnabled;
5220 int mId;
5221
5222 public String toString() {
5223 return mString;
5224 }
5225 }
5226
5227 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04005228 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005229 * and allow filtering.
5230 */
5231 private class MyArrayListAdapter extends ArrayAdapter<Container> {
5232 public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005233 super(context,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005234 multiple ? com.android.internal.R.layout.select_dialog_multichoice :
Cary Clarkd6982c92009-05-29 11:02:22 -04005235 com.android.internal.R.layout.select_dialog_singlechoice,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005236 objects);
5237 }
5238
5239 @Override
5240 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04005241 // AdapterView's onChanged method uses this to determine whether
5242 // to restore the old state. Return false so that the old (out
5243 // of date) state does not replace the new, valid state.
5244 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005245 }
5246
5247 private Container item(int position) {
5248 if (position < 0 || position >= getCount()) {
5249 return null;
5250 }
5251 return (Container) getItem(position);
5252 }
5253
5254 @Override
5255 public long getItemId(int position) {
5256 Container item = item(position);
5257 if (item == null) {
5258 return -1;
5259 }
5260 return item.mId;
5261 }
5262
5263 @Override
5264 public boolean areAllItemsEnabled() {
5265 return false;
5266 }
5267
5268 @Override
5269 public boolean isEnabled(int position) {
5270 Container item = item(position);
5271 if (item == null) {
5272 return false;
5273 }
5274 return item.mEnabled;
5275 }
5276 }
5277
5278 private InvokeListBox(String[] array,
5279 boolean[] enabled, int[] selected) {
5280 mMultiple = true;
5281 mSelectedArray = selected;
5282
5283 int length = array.length;
5284 mContainers = new Container[length];
5285 for (int i = 0; i < length; i++) {
5286 mContainers[i] = new Container();
5287 mContainers[i].mString = array[i];
5288 mContainers[i].mEnabled = enabled[i];
5289 mContainers[i].mId = i;
5290 }
5291 }
5292
Cary Clarkd6982c92009-05-29 11:02:22 -04005293 private InvokeListBox(String[] array, boolean[] enabled, int
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005294 selection) {
5295 mSelection = selection;
5296 mMultiple = false;
5297
5298 int length = array.length;
5299 mContainers = new Container[length];
5300 for (int i = 0; i < length; i++) {
5301 mContainers[i] = new Container();
5302 mContainers[i].mString = array[i];
5303 mContainers[i].mEnabled = enabled[i];
5304 mContainers[i].mId = i;
5305 }
5306 }
5307
Leon Scroggins3667ce42009-05-13 15:58:03 -04005308 /*
5309 * Whenever the data set changes due to filtering, this class ensures
5310 * that the checked item remains checked.
5311 */
5312 private class SingleDataSetObserver extends DataSetObserver {
5313 private long mCheckedId;
5314 private ListView mListView;
5315 private Adapter mAdapter;
5316
5317 /*
5318 * Create a new observer.
5319 * @param id The ID of the item to keep checked.
5320 * @param l ListView for getting and clearing the checked states
5321 * @param a Adapter for getting the IDs
5322 */
5323 public SingleDataSetObserver(long id, ListView l, Adapter a) {
5324 mCheckedId = id;
5325 mListView = l;
5326 mAdapter = a;
5327 }
5328
5329 public void onChanged() {
5330 // The filter may have changed which item is checked. Find the
5331 // item that the ListView thinks is checked.
5332 int position = mListView.getCheckedItemPosition();
5333 long id = mAdapter.getItemId(position);
5334 if (mCheckedId != id) {
5335 // Clear the ListView's idea of the checked item, since
5336 // it is incorrect
5337 mListView.clearChoices();
5338 // Search for mCheckedId. If it is in the filtered list,
5339 // mark it as checked
5340 int count = mAdapter.getCount();
5341 for (int i = 0; i < count; i++) {
5342 if (mAdapter.getItemId(i) == mCheckedId) {
5343 mListView.setItemChecked(i, true);
5344 break;
5345 }
5346 }
5347 }
5348 }
5349
5350 public void onInvalidate() {}
5351 }
5352
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005353 public void run() {
5354 final ListView listView = (ListView) LayoutInflater.from(mContext)
5355 .inflate(com.android.internal.R.layout.select_dialog, null);
Cary Clarkd6982c92009-05-29 11:02:22 -04005356 final MyArrayListAdapter adapter = new
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005357 MyArrayListAdapter(mContext, mContainers, mMultiple);
5358 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
5359 .setView(listView).setCancelable(true)
5360 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04005361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005362 if (mMultiple) {
5363 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
5364 public void onClick(DialogInterface dialog, int which) {
5365 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04005366 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005367 adapter.getCount(), 0,
5368 listView.getCheckedItemPositions());
5369 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07005370 b.setNegativeButton(android.R.string.cancel,
5371 new DialogInterface.OnClickListener() {
5372 public void onClick(DialogInterface dialog, int which) {
5373 mWebViewCore.sendMessage(
5374 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5375 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005376 }
5377 final AlertDialog dialog = b.create();
5378 listView.setAdapter(adapter);
5379 listView.setFocusableInTouchMode(true);
5380 // There is a bug (1250103) where the checks in a ListView with
5381 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04005382 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005383 // filtered. Do not allow filtering on multiple lists until
5384 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04005385
Leon Scroggins3667ce42009-05-13 15:58:03 -04005386 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005387 if (mMultiple) {
5388 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
5389 int length = mSelectedArray.length;
5390 for (int i = 0; i < length; i++) {
5391 listView.setItemChecked(mSelectedArray[i], true);
5392 }
5393 } else {
5394 listView.setOnItemClickListener(new OnItemClickListener() {
5395 public void onItemClick(AdapterView parent, View v,
5396 int position, long id) {
5397 mWebViewCore.sendMessage(
5398 EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
5399 dialog.dismiss();
5400 }
5401 });
5402 if (mSelection != -1) {
5403 listView.setSelection(mSelection);
5404 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
5405 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04005406 DataSetObserver observer = new SingleDataSetObserver(
5407 adapter.getItemId(mSelection), listView, adapter);
5408 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005409 }
5410 }
5411 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
5412 public void onCancel(DialogInterface dialog) {
5413 mWebViewCore.sendMessage(
5414 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5415 }
5416 });
5417 dialog.show();
5418 }
5419 }
5420
5421 /*
5422 * Request a dropdown menu for a listbox with multiple selection.
5423 *
5424 * @param array Labels for the listbox.
5425 * @param enabledArray Which positions are enabled.
5426 * @param selectedArray Which positions are initally selected.
5427 */
5428 void requestListBox(String[] array, boolean[]enabledArray, int[]
5429 selectedArray) {
5430 mPrivateHandler.post(
5431 new InvokeListBox(array, enabledArray, selectedArray));
5432 }
5433
5434 /*
5435 * Request a dropdown menu for a listbox with single selection or a single
5436 * <select> element.
5437 *
5438 * @param array Labels for the listbox.
5439 * @param enabledArray Which positions are enabled.
5440 * @param selection Which position is initally selected.
5441 */
5442 void requestListBox(String[] array, boolean[]enabledArray, int selection) {
5443 mPrivateHandler.post(
5444 new InvokeListBox(array, enabledArray, selection));
5445 }
5446
5447 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005448 private void sendMoveMouse(int frame, int node, int x, int y) {
5449 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
5450 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005451 }
5452
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005453 /*
5454 * Send a mouse move event to the webcore thread.
5455 *
5456 * @param removeFocus Pass true if the "mouse" cursor is now over a node
5457 * which wants key events, but it is not the focus. This
5458 * will make the visual appear as though nothing is in
5459 * focus. Remove the WebTextView, if present, and stop
5460 * drawing the blinking caret.
5461 * called by JNI
5462 */
5463 private void sendMoveMouseIfLatest(boolean removeFocus) {
5464 if (removeFocus) {
5465 clearTextEntry();
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04005466 setFocusControllerInactive();
Cary Clark19436562009-06-04 16:25:07 -04005467 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005468 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
5469 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005470 }
5471
5472 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005473 private void sendMotionUp(int touchGeneration,
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005474 int frame, int node, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005475 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
5476 touchUpData.mMoveGeneration = touchGeneration;
Cary Clarkd6982c92009-05-29 11:02:22 -04005477 touchUpData.mFrame = frame;
5478 touchUpData.mNode = node;
5479 touchUpData.mX = x;
5480 touchUpData.mY = y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005481 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
5482 }
5483
5484
5485 private int getScaledMaxXScroll() {
5486 int width;
5487 if (mHeightCanMeasure == false) {
5488 width = getViewWidth() / 4;
5489 } else {
5490 Rect visRect = new Rect();
5491 calcOurVisibleRect(visRect);
5492 width = visRect.width() / 2;
5493 }
5494 // FIXME the divisor should be retrieved from somewhere
5495 return viewToContent(width);
5496 }
5497
5498 private int getScaledMaxYScroll() {
5499 int height;
5500 if (mHeightCanMeasure == false) {
5501 height = getViewHeight() / 4;
5502 } else {
5503 Rect visRect = new Rect();
5504 calcOurVisibleRect(visRect);
5505 height = visRect.height() / 2;
5506 }
5507 // FIXME the divisor should be retrieved from somewhere
5508 // the closest thing today is hard-coded into ScrollView.java
5509 // (from ScrollView.java, line 363) int maxJump = height/2;
5510 return viewToContent(height);
5511 }
5512
5513 /**
5514 * Called by JNI to invalidate view
5515 */
5516 private void viewInvalidate() {
5517 invalidate();
5518 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005519
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005520 // return true if the key was handled
Cary Clark215b72c2009-06-26 14:38:43 -04005521 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
5522 long time, boolean ignorePlugin) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005523 if (mNativeClass == 0) {
5524 return false;
5525 }
Cary Clark215b72c2009-06-26 14:38:43 -04005526 if (ignorePlugin == false && nativePluginEatsNavKey()) {
5527 KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
5528 , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
5529 | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
5530 | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
5531 , 0, 0, 0);
5532 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
5533 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
5534 return true;
5535 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005536 mLastCursorTime = time;
5537 mLastCursorBounds = nativeGetCursorRingBounds();
5538 boolean keyHandled
5539 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005540 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005541 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
5542 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005543 + " handled=" + keyHandled);
5544 }
5545 if (keyHandled == false || mHeightCanMeasure == false) {
5546 return keyHandled;
5547 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005548 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
5549 if (contentCursorRingBounds.isEmpty()) return keyHandled;
5550 Rect viewCursorRingBounds = contentToView(contentCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005551 Rect visRect = new Rect();
5552 calcOurVisibleRect(visRect);
5553 Rect outset = new Rect(visRect);
5554 int maxXScroll = visRect.width() / 2;
5555 int maxYScroll = visRect.height() / 2;
5556 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005557 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005558 return keyHandled;
5559 }
5560 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005561 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
5562 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005563 if (maxH > 0) {
5564 pinScrollBy(maxH, 0, true, 0);
5565 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005566 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
5567 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005568 if (maxH < 0) {
5569 pinScrollBy(maxH, 0, true, 0);
5570 }
5571 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005572 if (mLastCursorBounds.isEmpty()) return keyHandled;
5573 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
5574 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005575 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005576 if (DebugFlags.WEB_VIEW) {
5577 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
5578 + contentCursorRingBounds);
5579 }
5580 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005581 mUserScroll = true;
5582 return keyHandled;
5583 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005584
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005585 /**
5586 * Set the background color. It's white by default. Pass
5587 * zero to make the view transparent.
5588 * @param color the ARGB color described by Color.java
5589 */
5590 public void setBackgroundColor(int color) {
5591 mBackgroundColor = color;
5592 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
5593 }
5594
5595 public void debugDump() {
5596 nativeDebugDump();
5597 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
5598 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005599
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005600 /**
5601 * Update our cache with updatedText.
5602 * @param updatedText The new text to put in our cache.
5603 */
5604 /* package */ void updateCachedTextfield(String updatedText) {
5605 // Also place our generation number so that when we look at the cache
5606 // we recognize that it is up to date.
5607 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
5608 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005609
Leon Scroggins4890feb2009-07-02 10:37:10 -04005610 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005611 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04005612 private native int nativeCursorFramePointer();
5613 private native Rect nativeCursorNodeBounds();
5614 /* package */ native int nativeCursorNodePointer();
5615 /* package */ native boolean nativeCursorMatchesFocus();
5616 private native boolean nativeCursorIntersects(Rect visibleRect);
5617 private native boolean nativeCursorIsAnchor();
Cary Clark215b72c2009-06-26 14:38:43 -04005618 private native boolean nativeCursorIsPlugin();
Cary Clarkd6982c92009-05-29 11:02:22 -04005619 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04005620 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04005621 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005622 /**
5623 * Returns true if the native cursor node says it wants to handle key events
5624 * (ala plugins). This can only be called if mNativeClass is non-zero!
5625 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04005626 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005627 private native void nativeDebugDump();
5628 private native void nativeDestroy();
Cary Clarkd6982c92009-05-29 11:02:22 -04005629 private native void nativeDrawCursorRing(Canvas content);
5630 private native void nativeDrawMatches(Canvas canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005631 private native void nativeDrawSelection(Canvas content
5632 , int x, int y, boolean extendSelection);
5633 private native void nativeDrawSelectionRegion(Canvas content);
Cary Clarkd6982c92009-05-29 11:02:22 -04005634 private native void nativeDumpDisplayTree(String urlOrNull);
5635 private native int nativeFindAll(String findLower, String findUpper);
5636 private native void nativeFindNext(boolean forward);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005637 private native boolean nativeFocusCandidateIsPassword();
5638 private native boolean nativeFocusCandidateIsRtlText();
5639 private native boolean nativeFocusCandidateIsTextField();
5640 private native boolean nativeFocusCandidateIsTextInput();
5641 private native int nativeFocusCandidateMaxLength();
Leon Scroggins0ca70882009-06-26 17:45:29 -04005642 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005643 private native Rect nativeFocusCandidateNodeBounds();
5644 /* package */ native int nativeFocusCandidatePointer();
5645 private native String nativeFocusCandidateText();
5646 private native int nativeFocusCandidateTextSize();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005647 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04005648 private native Rect nativeGetCursorRingBounds();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005649 private native Region nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04005650 private native boolean nativeHasCursorNode();
5651 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04005652 private native void nativeHideCursor();
Cary Clarkd6982c92009-05-29 11:02:22 -04005653 private native String nativeImageURI(int x, int y);
5654 private native void nativeInstrumentReport();
Leon Scroggins01058282009-07-30 16:33:56 -04005655 /* package */ native void nativeMoveCursorToNextTextInput();
Cary Clarkd6982c92009-05-29 11:02:22 -04005656 // return true if the page has been scrolled
5657 private native boolean nativeMotionUp(int x, int y, int slop);
5658 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005659 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04005660 boolean noScroll);
5661 private native int nativeMoveGeneration();
5662 private native void nativeMoveSelection(int x, int y,
5663 boolean extendSelection);
Cary Clark215b72c2009-06-26 14:38:43 -04005664 private native boolean nativePluginEatsNavKey();
Cary Clarkd6982c92009-05-29 11:02:22 -04005665 // Like many other of our native methods, you must make sure that
5666 // mNativeClass is not null before calling this method.
5667 private native void nativeRecordButtons(boolean focused,
5668 boolean pressed, boolean invalidate);
5669 private native void nativeSelectBestAt(Rect rect);
5670 private native void nativeSetFindIsDown();
5671 private native void nativeSetFollowedLink(boolean followed);
5672 private native void nativeSetHeightCanMeasure(boolean measure);
Leon Scroggins01058282009-07-30 16:33:56 -04005673 // Returns a value corresponding to CachedFrame::ImeAction
5674 /* package */ native int nativeTextFieldAction();
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005675 /**
5676 * Perform a click on a currently focused text input. Since it is already
5677 * focused, there is no need to go through the nativeMotionUp code, which
5678 * may change the Cursor.
5679 */
5680 private native void nativeTextInputMotionUp(int x, int y);
Cary Clarkd6982c92009-05-29 11:02:22 -04005681 private native int nativeTextGeneration();
5682 // Never call this version except by updateCachedTextfield(String) -
5683 // we always want to pass in our generation number.
5684 private native void nativeUpdateCachedTextfield(String updatedText,
5685 int generation);
Cary Clark215b72c2009-06-26 14:38:43 -04005686 private native void nativeUpdatePluginReceivesEvents();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005687 // return NO_LEFTEDGE means failure.
5688 private static final int NO_LEFTEDGE = -1;
Cary Clark77d98f42009-07-31 09:40:38 -04005689 private native int nativeGetBlockLeftEdge(int x, int y, float scale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005690}