blob: 05a7806f2950ef938adb735d922e9ca60836fae7 [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 // The list of loaded plugins.
310 private static PluginList sPluginList;
311
312 /**
313 * Position of the last touch event.
314 */
315 private float mLastTouchX;
316 private float mLastTouchY;
317
318 /**
319 * Time of the last touch event.
320 */
321 private long mLastTouchTime;
322
323 /**
324 * Time of the last time sending touch event to WebViewCore
325 */
326 private long mLastSentTouchTime;
327
328 /**
329 * The minimum elapsed time before sending another ACTION_MOVE event to
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700330 * WebViewCore. This really should be tuned for each type of the devices.
331 * For example in Google Map api test case, it takes Dream device at least
332 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
333 * triggering the layout and drawing the picture. While the same process
334 * takes 60+ms on the current high speed device. If we make
335 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
336 * to WebViewCore queue and the real layout and draw events will be pushed
337 * to further, which slows down the refresh rate. Choose 50 to favor the
338 * current high speed devices. For Dream like devices, 100 is a better
339 * choice. Maybe make this in the buildspec later.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800340 */
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700341 private static final int TOUCH_SENT_INTERVAL = 50;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342
343 /**
344 * Helper class to get velocity for fling
345 */
346 VelocityTracker mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700347 private int mMaximumFling;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800348
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700349 // use this flag to control whether enabling the new double tap zoom
350 static final boolean ENABLE_DOUBLETAP_ZOOM = true;
351
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352 /**
353 * Touch mode
354 */
355 private int mTouchMode = TOUCH_DONE_MODE;
356 private static final int TOUCH_INIT_MODE = 1;
357 private static final int TOUCH_DRAG_START_MODE = 2;
358 private static final int TOUCH_DRAG_MODE = 3;
359 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
360 private static final int TOUCH_SHORTPRESS_MODE = 5;
361 private static final int TOUCH_DOUBLECLICK_MODE = 6;
362 private static final int TOUCH_DONE_MODE = 7;
363 private static final int TOUCH_SELECT_MODE = 8;
364 // touch mode values specific to scale+scroll
365 private static final int FIRST_SCROLL_ZOOM = 9;
366 private static final int SCROLL_ZOOM_ANIMATION_IN = 9;
367 private static final int SCROLL_ZOOM_ANIMATION_OUT = 10;
368 private static final int SCROLL_ZOOM_OUT = 11;
369 private static final int LAST_SCROLL_ZOOM = 11;
370 // end of touch mode values specific to scale+scroll
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700371 private static final int TOUCH_DOUBLE_TAP_MODE = 12;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372
373 // Whether to forward the touch events to WebCore
374 private boolean mForwardTouchEvents = false;
375
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800376 // Whether to prevent drag during touch. The initial value depends on
377 // mForwardTouchEvents. If WebCore wants touch events, we assume it will
378 // take control of touch events unless it says no for touch down event.
379 private boolean mPreventDrag;
380
Leon Scroggins72543e12009-07-23 15:29:45 -0400381 // To keep track of whether the current drag was initiated by a WebTextView,
382 // so that we know not to hide the cursor
383 boolean mDragFromTextInput;
384
Cary Clarkd6982c92009-05-29 11:02:22 -0400385 // Whether or not to draw the cursor ring.
386 private boolean mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800387
Mike Reedd205d5b2009-05-27 11:02:29 -0400388 // true if onPause has been called (and not onResume)
389 private boolean mIsPaused;
390
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 /**
392 * Customizable constant
393 */
394 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
395 private int mTouchSlopSquare;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700396 // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
397 private int mDoubleTapSlopSquare;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700398 // pre-computed density adjusted navigation slop
399 private int mNavSlop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800400 // This should be ViewConfiguration.getTapTimeout()
401 // But system time out is 100ms, which is too short for the browser.
402 // In the browser, if it switches out of tap too soon, jump tap won't work.
403 private static final int TAP_TIMEOUT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 // This should be ViewConfiguration.getLongPressTimeout()
405 // But system time out is 500ms, which is too short for the browser.
406 // With a short timeout, it's difficult to treat trigger a short press.
407 private static final int LONG_PRESS_TIMEOUT = 1000;
408 // needed to avoid flinging after a pause of no movement
409 private static final int MIN_FLING_TIME = 250;
The Android Open Source Project10592532009-03-18 17:39:46 -0700410 // The time that the Zoom Controls are visible before fading away
Cary Clarkd6982c92009-05-29 11:02:22 -0400411 private static final long ZOOM_CONTROLS_TIMEOUT =
The Android Open Source Project10592532009-03-18 17:39:46 -0700412 ViewConfiguration.getZoomControlsTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 // The amount of content to overlap between two screens when going through
414 // pages with the space bar, in pixels.
415 private static final int PAGE_SCROLL_OVERLAP = 24;
416
417 /**
418 * These prevent calling requestLayout if either dimension is fixed. This
419 * depends on the layout parameters and the measure specs.
420 */
421 boolean mWidthCanMeasure;
422 boolean mHeightCanMeasure;
423
424 // Remember the last dimensions we sent to the native side so we can avoid
425 // sending the same dimensions more than once.
426 int mLastWidthSent;
427 int mLastHeightSent;
428
429 private int mContentWidth; // cache of value from WebViewCore
430 private int mContentHeight; // cache of value from WebViewCore
431
Cary Clarkd6982c92009-05-29 11:02:22 -0400432 // Need to have the separate control for horizontal and vertical scrollbar
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800433 // style than the View's single scrollbar style
434 private boolean mOverlayHorizontalScrollbar = true;
435 private boolean mOverlayVerticalScrollbar = false;
436
437 // our standard speed. this way small distances will be traversed in less
438 // time than large distances, but we cap the duration, so that very large
439 // distances won't take too long to get there.
440 private static final int STD_SPEED = 480; // pixels per second
441 // time for the longest scroll animation
442 private static final int MAX_DURATION = 750; // milliseconds
443 private Scroller mScroller;
444
445 private boolean mWrapContent;
446
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 /**
448 * Private message ids
449 */
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400450 private static final int REMEMBER_PASSWORD = 1;
451 private static final int NEVER_REMEMBER_PASSWORD = 2;
452 private static final int SWITCH_TO_SHORTPRESS = 3;
453 private static final int SWITCH_TO_LONGPRESS = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700454 private static final int RELEASE_SINGLE_TAP = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400455 private static final int REQUEST_FORM_DATA = 6;
456 private static final int SWITCH_TO_CLICK = 7;
457 private static final int RESUME_WEBCORE_UPDATE = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458
459 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400460 static final int SCROLL_TO_MSG_ID = 10;
461 static final int SCROLL_BY_MSG_ID = 11;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800462 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400463 static final int SPAWN_SCROLL_TO_MSG_ID = 12;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800464 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400465 static final int SYNC_SCROLL_TO_MSG_ID = 13;
466 static final int NEW_PICTURE_MSG_ID = 14;
467 static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
468 static final int WEBCORE_INITIALIZED_MSG_ID = 16;
469 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
Cary Clark215b72c2009-06-26 14:38:43 -0400470 static final int MOVE_OUT_OF_PLUGIN = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400471 static final int CLEAR_TEXT_ENTRY = 20;
472
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400473 static final int UPDATE_CLIPBOARD = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400474 static final int LONG_PRESS_CENTER = 23;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400475 static final int PREVENT_TOUCH_ID = 24;
476 static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 // obj=Rect in doc coordinates
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400478 static final int INVAL_RECT_MSG_ID = 26;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400479 static final int REQUEST_KEYBOARD = 27;
Cary Clarkd6982c92009-05-29 11:02:22 -0400480
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 static final String[] HandlerDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400482 "REMEMBER_PASSWORD", // = 1;
483 "NEVER_REMEMBER_PASSWORD", // = 2;
484 "SWITCH_TO_SHORTPRESS", // = 3;
485 "SWITCH_TO_LONGPRESS", // = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700486 "RELEASE_SINGLE_TAP", // = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400487 "REQUEST_FORM_DATA", // = 6;
488 "SWITCH_TO_CLICK", // = 7;
489 "RESUME_WEBCORE_UPDATE", // = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 "9",
491 "SCROLL_TO_MSG_ID", // = 10;
492 "SCROLL_BY_MSG_ID", // = 11;
493 "SPAWN_SCROLL_TO_MSG_ID", // = 12;
494 "SYNC_SCROLL_TO_MSG_ID", // = 13;
495 "NEW_PICTURE_MSG_ID", // = 14;
496 "UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
497 "WEBCORE_INITIALIZED_MSG_ID", // = 16;
498 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
Grace Klobaef347ef2009-07-30 11:20:32 -0700499 "18", // = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400500 "MOVE_OUT_OF_PLUGIN", // = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400501 "CLEAR_TEXT_ENTRY", // = 20;
Leon Scrogginsfd06bc82009-06-08 13:22:02 -0400502 "21", // = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800503 "UPDATE_CLIPBOARD", // = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400504 "LONG_PRESS_CENTER", // = 23;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800505 "PREVENT_TOUCH_ID", // = 24;
506 "WEBCORE_NEED_TOUCH_EVENTS", // = 25;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400507 "INVAL_RECT_MSG_ID", // = 26;
508 "REQUEST_KEYBOARD" // = 27;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800509 };
510
511 // width which view is considered to be fully zoomed out
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700512 static final int ZOOM_OUT_WIDTH = 1008;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513
Grace Kloba25737912009-06-19 12:42:47 -0700514 // default scale limit. Depending on the display density
515 private static float DEFAULT_MAX_ZOOM_SCALE;
516 private static float DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800517 // scale limit, which can be set through viewport meta tag in the web page
Grace Kloba25737912009-06-19 12:42:47 -0700518 private float mMaxZoomScale;
519 private float mMinZoomScale;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700520 private boolean mMinZoomScaleFixed = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800521
522 // initial scale in percent. 0 means using default.
523 private int mInitialScale = 0;
524
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700525 // while in the zoom overview mode, the page's width is fully fit to the
526 // current window. The page is alive, in another words, you can click to
527 // follow the links. Double tap will toggle between zoom overview mode and
528 // the last zoom scale.
529 boolean mInZoomOverview = false;
530 // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
531 // engadget always have wider mContentWidth no matter what viewport size is.
532 int mZoomOverviewWidth = 0;
533 float mLastScale;
534
Grace Kloba25737912009-06-19 12:42:47 -0700535 // default scale. Depending on the display density.
536 static int DEFAULT_SCALE_PERCENT;
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700537 private float mDefaultScale;
Grace Kloba25737912009-06-19 12:42:47 -0700538
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800539 // set to true temporarily while the zoom control is being dragged
540 private boolean mPreviewZoomOnly = false;
541
542 // computed scale and inverse, from mZoomWidth.
Grace Kloba25737912009-06-19 12:42:47 -0700543 private float mActualScale;
544 private float mInvActualScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 // if this is non-zero, it is used on drawing rather than mActualScale
546 private float mZoomScale;
547 private float mInvInitialZoomScale;
548 private float mInvFinalZoomScale;
Grace Kloba675c7d22009-07-23 09:21:21 -0700549 private int mInitialScrollX;
550 private int mInitialScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 private long mZoomStart;
552 private static final int ZOOM_ANIMATION_LENGTH = 500;
553
554 private boolean mUserScroll = false;
555
556 private int mSnapScrollMode = SNAP_NONE;
557 private static final int SNAP_NONE = 1;
558 private static final int SNAP_X = 2;
559 private static final int SNAP_Y = 3;
560 private static final int SNAP_X_LOCK = 4;
561 private static final int SNAP_Y_LOCK = 5;
562 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 // Used to match key downs and key ups
565 private boolean mGotKeyDown;
566
567 /* package */ static boolean mLogEvent = true;
568 private static final int EVENT_LOG_ZOOM_LEVEL_CHANGE = 70101;
569 private static final int EVENT_LOG_DOUBLE_TAP_DURATION = 70102;
570
571 // for event log
572 private long mLastTouchUpTime = 0;
573
574 /**
575 * URI scheme for telephone number
576 */
577 public static final String SCHEME_TEL = "tel:";
578 /**
579 * URI scheme for email address
580 */
581 public static final String SCHEME_MAILTO = "mailto:";
582 /**
583 * URI scheme for map address
584 */
585 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400586
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 private int mBackgroundColor = Color.WHITE;
588
589 // Used to notify listeners of a new picture.
590 private PictureListener mPictureListener;
591 /**
592 * Interface to listen for new pictures as they change.
593 */
594 public interface PictureListener {
595 /**
596 * Notify the listener that the picture has changed.
597 * @param view The WebView that owns the picture.
598 * @param picture The new picture.
599 */
600 public void onNewPicture(WebView view, Picture picture);
601 }
602
Leon Scroggins3246c222009-05-26 09:51:23 -0400603 // FIXME: Want to make this public, but need to change the API file.
604 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800605 /**
606 * Default HitTestResult, where the target is unknown
607 */
608 public static final int UNKNOWN_TYPE = 0;
609 /**
610 * HitTestResult for hitting a HTML::a tag
611 */
612 public static final int ANCHOR_TYPE = 1;
613 /**
614 * HitTestResult for hitting a phone number
615 */
616 public static final int PHONE_TYPE = 2;
617 /**
618 * HitTestResult for hitting a map address
619 */
620 public static final int GEO_TYPE = 3;
621 /**
622 * HitTestResult for hitting an email address
623 */
624 public static final int EMAIL_TYPE = 4;
625 /**
626 * HitTestResult for hitting an HTML::img tag
627 */
628 public static final int IMAGE_TYPE = 5;
629 /**
630 * HitTestResult for hitting a HTML::a tag which contains HTML::img
631 */
632 public static final int IMAGE_ANCHOR_TYPE = 6;
633 /**
634 * HitTestResult for hitting a HTML::a tag with src=http
635 */
636 public static final int SRC_ANCHOR_TYPE = 7;
637 /**
638 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
639 */
640 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
641 /**
642 * HitTestResult for hitting an edit text area
643 */
644 public static final int EDIT_TEXT_TYPE = 9;
645
646 private int mType;
647 private String mExtra;
648
649 HitTestResult() {
650 mType = UNKNOWN_TYPE;
651 }
652
653 private void setType(int type) {
654 mType = type;
655 }
656
657 private void setExtra(String extra) {
658 mExtra = extra;
659 }
660
661 public int getType() {
662 return mType;
663 }
664
665 public String getExtra() {
666 return mExtra;
667 }
668 }
669
The Android Open Source Project10592532009-03-18 17:39:46 -0700670 // The View containing the zoom controls
671 private ExtendedZoomControls mZoomControls;
672 private Runnable mZoomControlRunnable;
673
Cary Clarkd6982c92009-05-29 11:02:22 -0400674 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700676 // These keep track of the center point of the zoom. They are used to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 // determine the point around which we should zoom.
678 private float mZoomCenterX;
679 private float mZoomCenterY;
680
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700681 private ZoomButtonsController.OnZoomListener mZoomListener =
682 new ZoomButtonsController.OnZoomListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 public void onVisibilityChanged(boolean visible) {
685 if (visible) {
686 switchOutDrawHistory();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700687 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800688 }
689 }
690
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700691 public void onZoom(boolean zoomIn) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800692 if (zoomIn) {
693 zoomIn();
694 } else {
695 zoomOut();
696 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400697
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700698 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 };
Cary Clarkd6982c92009-05-29 11:02:22 -0400701
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 /**
703 * Construct a new WebView with a Context object.
704 * @param context A Context object used to access application assets.
705 */
706 public WebView(Context context) {
707 this(context, null);
708 }
709
710 /**
711 * Construct a new WebView with layout parameters.
712 * @param context A Context object used to access application assets.
713 * @param attrs An AttributeSet passed to our parent.
714 */
715 public WebView(Context context, AttributeSet attrs) {
716 this(context, attrs, com.android.internal.R.attr.webViewStyle);
717 }
718
719 /**
720 * Construct a new WebView with layout parameters and a default style.
721 * @param context A Context object used to access application assets.
722 * @param attrs An AttributeSet passed to our parent.
723 * @param defStyle The default style resource ID.
724 */
725 public WebView(Context context, AttributeSet attrs, int defStyle) {
726 super(context, attrs, defStyle);
727 init();
728
729 mCallbackProxy = new CallbackProxy(context, this);
730 mWebViewCore = new WebViewCore(context, this, mCallbackProxy);
731 mDatabase = WebViewDatabase.getInstance(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800732 mScroller = new Scroller(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700733
Patrick Scott0a5ce012009-07-02 08:56:10 -0400734 mViewManager = new ViewManager(this);
735
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700736 mZoomButtonsController = new ZoomButtonsController(this);
737 mZoomButtonsController.setOnZoomListener(mZoomListener);
Leon Scrogginsaa3f96a2009-06-11 14:46:35 -0400738 // ZoomButtonsController positions the buttons at the bottom, but in
739 // the middle. Change their layout parameters so they appear on the
740 // right.
741 View controls = mZoomButtonsController.getZoomControls();
742 ViewGroup.LayoutParams params = controls.getLayoutParams();
743 if (params instanceof FrameLayout.LayoutParams) {
744 FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
745 params;
746 frameParams.gravity = Gravity.RIGHT;
747 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700748 }
749
750 private void updateZoomButtonsEnabled() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700751 boolean canZoomIn = mActualScale < mMaxZoomScale;
752 boolean canZoomOut = mActualScale > mMinZoomScale;
753 if (!canZoomIn && !canZoomOut) {
754 // Hide the zoom in and out buttons, as well as the fit to page
755 // button, if the page cannot zoom
756 mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700757 } else {
758 // Bring back the hidden zoom controls.
759 mZoomButtonsController.getZoomControls()
760 .setVisibility(View.VISIBLE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700761 // Set each one individually, as a page may be able to zoom in
762 // or out.
763 mZoomButtonsController.setZoomInEnabled(canZoomIn);
764 mZoomButtonsController.setZoomOutEnabled(canZoomOut);
The Android Open Source Project10592532009-03-18 17:39:46 -0700765 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766 }
767
768 private void init() {
769 setWillNotDraw(false);
770 setFocusable(true);
771 setFocusableInTouchMode(true);
772 setClickable(true);
773 setLongClickable(true);
774
Romain Guy4296fc42009-07-06 11:48:52 -0700775 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700776 int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 mTouchSlopSquare = slop * slop;
778 mMinLockSnapReverseDistance = slop;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700779 slop = configuration.getScaledDoubleTapSlop();
780 mDoubleTapSlopSquare = slop * slop;
Grace Kloba25737912009-06-19 12:42:47 -0700781 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700782 // use one line height, 16 based on our current default font, for how
783 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -0700784 mNavSlop = (int) (16 * density);
785 // density adjusted scale factors
786 DEFAULT_SCALE_PERCENT = (int) (100 * density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700787 mDefaultScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700788 mActualScale = density;
789 mInvActualScale = 1 / density;
790 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
791 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
792 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
793 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
Romain Guy4296fc42009-07-06 11:48:52 -0700794 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 }
796
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700797 /* package */void updateDefaultZoomDensity(int zoomDensity) {
798 final float density = getContext().getResources().getDisplayMetrics().density
799 * 100 / zoomDensity;
800 if (Math.abs(density - mDefaultScale) > 0.01) {
801 float scaleFactor = density / mDefaultScale;
802 // adjust the limits
803 mNavSlop = (int) (16 * density);
804 DEFAULT_SCALE_PERCENT = (int) (100 * density);
805 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
806 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
807 mDefaultScale = density;
808 mMaxZoomScale *= scaleFactor;
809 mMinZoomScale *= scaleFactor;
810 setNewZoomScale(mActualScale * scaleFactor, false);
811 }
812 }
813
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 /* package */ boolean onSavePassword(String schemePlusHost, String username,
815 String password, final Message resumeMsg) {
816 boolean rVal = false;
817 if (resumeMsg == null) {
818 // null resumeMsg implies saving password silently
819 mDatabase.setUsernamePassword(schemePlusHost, username, password);
820 } else {
821 final Message remember = mPrivateHandler.obtainMessage(
822 REMEMBER_PASSWORD);
823 remember.getData().putString("host", schemePlusHost);
824 remember.getData().putString("username", username);
825 remember.getData().putString("password", password);
826 remember.obj = resumeMsg;
827
828 final Message neverRemember = mPrivateHandler.obtainMessage(
829 NEVER_REMEMBER_PASSWORD);
830 neverRemember.getData().putString("host", schemePlusHost);
831 neverRemember.getData().putString("username", username);
832 neverRemember.getData().putString("password", password);
833 neverRemember.obj = resumeMsg;
834
835 new AlertDialog.Builder(getContext())
836 .setTitle(com.android.internal.R.string.save_password_label)
837 .setMessage(com.android.internal.R.string.save_password_message)
838 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
839 new DialogInterface.OnClickListener() {
840 public void onClick(DialogInterface dialog, int which) {
841 resumeMsg.sendToTarget();
842 }
843 })
844 .setNeutralButton(com.android.internal.R.string.save_password_remember,
845 new DialogInterface.OnClickListener() {
846 public void onClick(DialogInterface dialog, int which) {
847 remember.sendToTarget();
848 }
849 })
850 .setNegativeButton(com.android.internal.R.string.save_password_never,
851 new DialogInterface.OnClickListener() {
852 public void onClick(DialogInterface dialog, int which) {
853 neverRemember.sendToTarget();
854 }
855 })
856 .setOnCancelListener(new OnCancelListener() {
857 public void onCancel(DialogInterface dialog) {
858 resumeMsg.sendToTarget();
859 }
860 }).show();
861 // Return true so that WebViewCore will pause while the dialog is
862 // up.
863 rVal = true;
864 }
865 return rVal;
866 }
867
868 @Override
869 public void setScrollBarStyle(int style) {
870 if (style == View.SCROLLBARS_INSIDE_INSET
871 || style == View.SCROLLBARS_OUTSIDE_INSET) {
872 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
873 } else {
874 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
875 }
876 super.setScrollBarStyle(style);
877 }
878
879 /**
880 * Specify whether the horizontal scrollbar has overlay style.
881 * @param overlay TRUE if horizontal scrollbar should have overlay style.
882 */
883 public void setHorizontalScrollbarOverlay(boolean overlay) {
884 mOverlayHorizontalScrollbar = overlay;
885 }
886
887 /**
888 * Specify whether the vertical scrollbar has overlay style.
889 * @param overlay TRUE if vertical scrollbar should have overlay style.
890 */
891 public void setVerticalScrollbarOverlay(boolean overlay) {
892 mOverlayVerticalScrollbar = overlay;
893 }
894
895 /**
896 * Return whether horizontal scrollbar has overlay style
897 * @return TRUE if horizontal scrollbar has overlay style.
898 */
899 public boolean overlayHorizontalScrollbar() {
900 return mOverlayHorizontalScrollbar;
901 }
902
903 /**
904 * Return whether vertical scrollbar has overlay style
905 * @return TRUE if vertical scrollbar has overlay style.
906 */
907 public boolean overlayVerticalScrollbar() {
908 return mOverlayVerticalScrollbar;
909 }
910
911 /*
912 * Return the width of the view where the content of WebView should render
913 * to.
914 */
915 private int getViewWidth() {
916 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
917 return getWidth();
918 } else {
919 return getWidth() - getVerticalScrollbarWidth();
920 }
921 }
922
923 /*
924 * Return the height of the view where the content of WebView should render
925 * to.
926 */
927 private int getViewHeight() {
928 if (!isHorizontalScrollBarEnabled() || mOverlayHorizontalScrollbar) {
929 return getHeight();
930 } else {
931 return getHeight() - getHorizontalScrollbarHeight();
932 }
933 }
934
935 /**
936 * @return The SSL certificate for the main top-level page or null if
937 * there is no certificate (the site is not secure).
938 */
939 public SslCertificate getCertificate() {
940 return mCertificate;
941 }
942
943 /**
944 * Sets the SSL certificate for the main top-level page.
945 */
946 public void setCertificate(SslCertificate certificate) {
947 // here, the certificate can be null (if the site is not secure)
948 mCertificate = certificate;
949 }
950
951 //-------------------------------------------------------------------------
952 // Methods called by activity
953 //-------------------------------------------------------------------------
954
955 /**
956 * Save the username and password for a particular host in the WebView's
957 * internal database.
958 * @param host The host that required the credentials.
959 * @param username The username for the given host.
960 * @param password The password for the given host.
961 */
962 public void savePassword(String host, String username, String password) {
963 mDatabase.setUsernamePassword(host, username, password);
964 }
965
966 /**
967 * Set the HTTP authentication credentials for a given host and realm.
968 *
969 * @param host The host for the credentials.
970 * @param realm The realm for the credentials.
971 * @param username The username for the password. If it is null, it means
972 * password can't be saved.
973 * @param password The password
974 */
975 public void setHttpAuthUsernamePassword(String host, String realm,
976 String username, String password) {
977 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
978 }
979
980 /**
981 * Retrieve the HTTP authentication username and password for a given
982 * host & realm pair
983 *
984 * @param host The host for which the credentials apply.
985 * @param realm The realm for which the credentials apply.
986 * @return String[] if found, String[0] is username, which can be null and
987 * String[1] is password. Return null if it can't find anything.
988 */
989 public String[] getHttpAuthUsernamePassword(String host, String realm) {
990 return mDatabase.getHttpAuthUsernamePassword(host, realm);
991 }
992
993 /**
994 * Destroy the internal state of the WebView. This method should be called
995 * after the WebView has been removed from the view system. No other
996 * methods may be called on a WebView after destroy.
997 */
998 public void destroy() {
999 clearTextEntry();
1000 if (mWebViewCore != null) {
1001 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -04001002 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 mCallbackProxy.setWebViewClient(null);
1004 mCallbackProxy.setWebChromeClient(null);
1005 // Tell WebViewCore to destroy itself
1006 WebViewCore webViewCore = mWebViewCore;
1007 mWebViewCore = null; // prevent using partial webViewCore
1008 webViewCore.destroy();
1009 // Remove any pending messages that might not be serviced yet.
1010 mPrivateHandler.removeCallbacksAndMessages(null);
1011 mCallbackProxy.removeCallbacksAndMessages(null);
1012 // Wake up the WebCore thread just in case it is waiting for a
1013 // javascript dialog.
1014 synchronized (mCallbackProxy) {
1015 mCallbackProxy.notify();
1016 }
1017 }
1018 if (mNativeClass != 0) {
1019 nativeDestroy();
1020 mNativeClass = 0;
1021 }
1022 }
1023
1024 /**
1025 * Enables platform notifications of data state and proxy changes.
1026 */
1027 public static void enablePlatformNotifications() {
1028 Network.enablePlatformNotifications();
1029 }
1030
1031 /**
1032 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001033 * from the Activity's onPause() or onStop().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 */
1035 public static void disablePlatformNotifications() {
1036 Network.disablePlatformNotifications();
1037 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 /**
Feng Qianb3081372009-06-29 15:55:18 -07001040 * Sets JavaScript engine flags.
1041 *
1042 * @param flags JS engine flags in a String
1043 *
1044 * @hide pending API solidification
1045 */
1046 public void setJsFlags(String flags) {
1047 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1048 }
1049
1050 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051 * Inform WebView of the network state. This is used to set
1052 * the javascript property window.navigator.isOnline and
1053 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1054 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001055 */
1056 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001057 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1058 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001059 }
1060
1061 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001062 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1064 * method no longer stores the display data for this WebView. The previous
1065 * behavior could potentially leak files if {@link #restoreState} was never
1066 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1067 * and restoring the display data.
1068 * @param outState The Bundle to store the WebView state.
1069 * @return The same copy of the back/forward list used to save the state. If
1070 * saveState fails, the returned list will be null.
1071 * @see #savePicture
1072 * @see #restorePicture
1073 */
1074 public WebBackForwardList saveState(Bundle outState) {
1075 if (outState == null) {
1076 return null;
1077 }
1078 // We grab a copy of the back/forward list because a client of WebView
1079 // may have invalidated the history list by calling clearHistory.
1080 WebBackForwardList list = copyBackForwardList();
1081 final int currentIndex = list.getCurrentIndex();
1082 final int size = list.getSize();
1083 // We should fail saving the state if the list is empty or the index is
1084 // not in a valid range.
1085 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1086 return null;
1087 }
1088 outState.putInt("index", currentIndex);
1089 // FIXME: This should just be a byte[][] instead of ArrayList but
1090 // Parcel.java does not have the code to handle multi-dimensional
1091 // arrays.
1092 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1093 for (int i = 0; i < size; i++) {
1094 WebHistoryItem item = list.getItemAtIndex(i);
1095 byte[] data = item.getFlattenedData();
1096 if (data == null) {
1097 // It would be very odd to not have any data for a given history
1098 // item. And we will fail to rebuild the history list without
1099 // flattened data.
1100 return null;
1101 }
1102 history.add(data);
1103 }
1104 outState.putSerializable("history", history);
1105 if (mCertificate != null) {
1106 outState.putBundle("certificate",
1107 SslCertificate.saveState(mCertificate));
1108 }
1109 return list;
1110 }
1111
1112 /**
1113 * Save the current display data to the Bundle given. Used in conjunction
1114 * with {@link #saveState}.
1115 * @param b A Bundle to store the display data.
1116 * @param dest The file to store the serialized picture data. Will be
1117 * overwritten with this WebView's picture data.
1118 * @return True if the picture was successfully saved.
1119 */
1120 public boolean savePicture(Bundle b, File dest) {
1121 if (dest == null || b == null) {
1122 return false;
1123 }
1124 final Picture p = capturePicture();
1125 try {
1126 final FileOutputStream out = new FileOutputStream(dest);
1127 p.writeToStream(out);
1128 out.close();
1129 } catch (FileNotFoundException e){
1130 e.printStackTrace();
1131 } catch (IOException e) {
1132 e.printStackTrace();
1133 } catch (RuntimeException e) {
1134 e.printStackTrace();
1135 }
1136 if (dest.length() > 0) {
1137 b.putInt("scrollX", mScrollX);
1138 b.putInt("scrollY", mScrollY);
1139 b.putFloat("scale", mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001140 if (mInZoomOverview) {
1141 b.putFloat("lastScale", mLastScale);
1142 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 return true;
1144 }
1145 return false;
1146 }
1147
1148 /**
1149 * Restore the display data that was save in {@link #savePicture}. Used in
1150 * conjunction with {@link #restoreState}.
1151 * @param b A Bundle containing the saved display data.
1152 * @param src The file where the picture data was stored.
1153 * @return True if the picture was successfully restored.
1154 */
1155 public boolean restorePicture(Bundle b, File src) {
1156 if (src == null || b == null) {
1157 return false;
1158 }
1159 if (src.exists()) {
1160 Picture p = null;
1161 try {
1162 final FileInputStream in = new FileInputStream(src);
1163 p = Picture.createFromStream(in);
1164 in.close();
1165 } catch (FileNotFoundException e){
1166 e.printStackTrace();
1167 } catch (RuntimeException e) {
1168 e.printStackTrace();
1169 } catch (IOException e) {
1170 e.printStackTrace();
1171 }
1172 if (p != null) {
1173 int sx = b.getInt("scrollX", 0);
1174 int sy = b.getInt("scrollY", 0);
1175 float scale = b.getFloat("scale", 1.0f);
1176 mDrawHistory = true;
1177 mHistoryPicture = p;
1178 mScrollX = sx;
1179 mScrollY = sy;
1180 mHistoryWidth = Math.round(p.getWidth() * scale);
1181 mHistoryHeight = Math.round(p.getHeight() * scale);
1182 // as getWidth() / getHeight() of the view are not
1183 // available yet, set up mActualScale, so that when
1184 // onSizeChanged() is called, the rest will be set
1185 // correctly
1186 mActualScale = scale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001187 float lastScale = b.getFloat("lastScale", -1.0f);
1188 if (lastScale > 0) {
1189 mInZoomOverview = true;
1190 mLastScale = lastScale;
1191 } else {
1192 mInZoomOverview = false;
1193 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 invalidate();
1195 return true;
1196 }
1197 }
1198 return false;
1199 }
1200
1201 /**
1202 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001203 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1204 * be called to restore the state of the WebView before using the object. If
1205 * it is called after the WebView has had a chance to build state (load
1206 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 * side-effects. Please note that this method no longer restores the
1208 * display data for this WebView. See {@link #savePicture} and {@link
1209 * #restorePicture} for saving and restoring the display data.
1210 * @param inState The incoming Bundle of state.
1211 * @return The restored back/forward list or null if restoreState failed.
1212 * @see #savePicture
1213 * @see #restorePicture
1214 */
1215 public WebBackForwardList restoreState(Bundle inState) {
1216 WebBackForwardList returnList = null;
1217 if (inState == null) {
1218 return returnList;
1219 }
1220 if (inState.containsKey("index") && inState.containsKey("history")) {
1221 mCertificate = SslCertificate.restoreState(
1222 inState.getBundle("certificate"));
1223
1224 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1225 final int index = inState.getInt("index");
1226 // We can't use a clone of the list because we need to modify the
1227 // shared copy, so synchronize instead to prevent concurrent
1228 // modifications.
1229 synchronized (list) {
1230 final List<byte[]> history =
1231 (List<byte[]>) inState.getSerializable("history");
1232 final int size = history.size();
1233 // Check the index bounds so we don't crash in native code while
1234 // restoring the history index.
1235 if (index < 0 || index >= size) {
1236 return null;
1237 }
1238 for (int i = 0; i < size; i++) {
1239 byte[] data = history.remove(0);
1240 if (data == null) {
1241 // If we somehow have null data, we cannot reconstruct
1242 // the item and thus our history list cannot be rebuilt.
1243 return null;
1244 }
1245 WebHistoryItem item = new WebHistoryItem(data);
1246 list.addHistoryItem(item);
1247 }
1248 // Grab the most recent copy to return to the caller.
1249 returnList = copyBackForwardList();
1250 // Update the copy to have the correct index.
1251 returnList.setCurrentIndex(index);
1252 }
1253 // Remove all pending messages because we are restoring previous
1254 // state.
1255 mWebViewCore.removeMessages();
1256 // Send a restore state message.
1257 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1258 }
1259 return returnList;
1260 }
1261
1262 /**
1263 * Load the given url.
1264 * @param url The url of the resource to load.
1265 */
1266 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001267 if (url == null) {
1268 return;
1269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 switchOutDrawHistory();
1271 mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
1272 clearTextEntry();
1273 }
1274
1275 /**
Grace Kloba57534302009-05-22 18:55:02 -07001276 * Load the url with postData using "POST" method into the WebView. If url
1277 * is not a network url, it will be loaded with {link
1278 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001279 *
Grace Kloba57534302009-05-22 18:55:02 -07001280 * @param url The url of the resource to load.
1281 * @param postData The data will be passed to "POST" request.
Cary Clarkd6982c92009-05-29 11:02:22 -04001282 *
Grace Kloba57534302009-05-22 18:55:02 -07001283 * @hide pending API solidification
1284 */
1285 public void postUrl(String url, byte[] postData) {
1286 if (URLUtil.isNetworkUrl(url)) {
1287 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001288 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1289 arg.mUrl = url;
1290 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001291 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
1292 clearTextEntry();
1293 } else {
1294 loadUrl(url);
1295 }
1296 }
1297
1298 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 * Load the given data into the WebView. This will load the data into
1300 * WebView using the data: scheme. Content loaded through this mechanism
1301 * does not have the ability to load content from the network.
1302 * @param data A String of data in the given encoding.
1303 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1304 * @param encoding The encoding of the data. i.e. utf-8, base64
1305 */
1306 public void loadData(String data, String mimeType, String encoding) {
1307 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1308 }
1309
1310 /**
1311 * Load the given data into the WebView, use the provided URL as the base
1312 * URL for the content. The base URL is the URL that represents the page
1313 * that is loaded through this interface. As such, it is used for the
1314 * history entry and to resolve any relative URLs. The failUrl is used if
1315 * browser fails to load the data provided. If it is empty or null, and the
1316 * load fails, then no history entry is created.
1317 * <p>
1318 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1319 * files through "file:///android_asset/" for the sub resources is more
1320 * restricted. If you provide null or empty string as baseUrl, you won't be
1321 * able to access asset files. If the baseUrl is anything other than
1322 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1323 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001324 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 * @param baseUrl Url to resolve relative paths with, if null defaults to
1326 * "about:blank"
1327 * @param data A String of data in the given encoding.
1328 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1329 * defaults to "text/html"
1330 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
1331 * @param failUrl URL to use if the content fails to load or null.
1332 */
1333 public void loadDataWithBaseURL(String baseUrl, String data,
1334 String mimeType, String encoding, String failUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001335
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1337 loadData(data, mimeType, encoding);
1338 return;
1339 }
1340 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001341 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1342 arg.mBaseUrl = baseUrl;
1343 arg.mData = data;
1344 arg.mMimeType = mimeType;
1345 arg.mEncoding = encoding;
1346 arg.mFailUrl = failUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
1348 clearTextEntry();
1349 }
1350
1351 /**
1352 * Stop the current load.
1353 */
1354 public void stopLoading() {
1355 // TODO: should we clear all the messages in the queue before sending
1356 // STOP_LOADING?
1357 switchOutDrawHistory();
1358 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1359 }
1360
1361 /**
1362 * Reload the current url.
1363 */
1364 public void reload() {
1365 switchOutDrawHistory();
1366 mWebViewCore.sendMessage(EventHub.RELOAD);
1367 }
1368
1369 /**
1370 * Return true if this WebView has a back history item.
1371 * @return True iff this WebView has a back history item.
1372 */
1373 public boolean canGoBack() {
1374 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1375 synchronized (l) {
1376 if (l.getClearPending()) {
1377 return false;
1378 } else {
1379 return l.getCurrentIndex() > 0;
1380 }
1381 }
1382 }
1383
1384 /**
1385 * Go back in the history of this WebView.
1386 */
1387 public void goBack() {
1388 goBackOrForward(-1);
1389 }
1390
1391 /**
1392 * Return true if this WebView has a forward history item.
1393 * @return True iff this Webview has a forward history item.
1394 */
1395 public boolean canGoForward() {
1396 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1397 synchronized (l) {
1398 if (l.getClearPending()) {
1399 return false;
1400 } else {
1401 return l.getCurrentIndex() < l.getSize() - 1;
1402 }
1403 }
1404 }
1405
1406 /**
1407 * Go forward in the history of this WebView.
1408 */
1409 public void goForward() {
1410 goBackOrForward(1);
1411 }
1412
1413 /**
1414 * Return true if the page can go back or forward the given
1415 * number of steps.
1416 * @param steps The negative or positive number of steps to move the
1417 * history.
1418 */
1419 public boolean canGoBackOrForward(int steps) {
1420 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1421 synchronized (l) {
1422 if (l.getClearPending()) {
1423 return false;
1424 } else {
1425 int newIndex = l.getCurrentIndex() + steps;
1426 return newIndex >= 0 && newIndex < l.getSize();
1427 }
1428 }
1429 }
1430
1431 /**
1432 * Go to the history item that is the number of steps away from
1433 * the current item. Steps is negative if backward and positive
1434 * if forward.
1435 * @param steps The number of steps to take back or forward in the back
1436 * forward list.
1437 */
1438 public void goBackOrForward(int steps) {
1439 goBackOrForward(steps, false);
1440 }
1441
1442 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
1443 // every time we go back or forward, we want to reset the
1444 // WebView certificate:
1445 // if the new site is secure, we will reload it and get a
1446 // new certificate set;
1447 // if the new site is not secure, the certificate must be
1448 // null, and that will be the case
1449 mCertificate = null;
1450 if (steps != 0) {
1451 clearTextEntry();
1452 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
1453 ignoreSnapshot ? 1 : 0);
1454 }
1455 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001456
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001457 private boolean extendScroll(int y) {
1458 int finalY = mScroller.getFinalY();
1459 int newY = pinLocY(finalY + y);
1460 if (newY == finalY) return false;
1461 mScroller.setFinalY(newY);
1462 mScroller.extendDuration(computeDuration(0, y));
1463 return true;
1464 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 /**
1467 * Scroll the contents of the view up by half the view size
1468 * @param top true to jump to the top of the page
1469 * @return true if the page was scrolled
1470 */
1471 public boolean pageUp(boolean top) {
1472 if (mNativeClass == 0) {
1473 return false;
1474 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001475 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 if (top) {
1477 // go to the top of the document
1478 return pinScrollTo(mScrollX, 0, true, 0);
1479 }
1480 // Page up
1481 int h = getHeight();
1482 int y;
1483 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1484 y = -h + PAGE_SCROLL_OVERLAP;
1485 } else {
1486 y = -h / 2;
1487 }
1488 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001489 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001490 : extendScroll(y);
1491 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001492
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001493 /**
1494 * Scroll the contents of the view down by half the page size
1495 * @param bottom true to jump to bottom of page
1496 * @return true if the page was scrolled
1497 */
1498 public boolean pageDown(boolean bottom) {
1499 if (mNativeClass == 0) {
1500 return false;
1501 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001502 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001503 if (bottom) {
1504 return pinScrollTo(mScrollX, mContentHeight, true, 0);
1505 }
1506 // Page down.
1507 int h = getHeight();
1508 int y;
1509 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1510 y = h - PAGE_SCROLL_OVERLAP;
1511 } else {
1512 y = h / 2;
1513 }
1514 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001515 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001516 : extendScroll(y);
1517 }
1518
1519 /**
1520 * Clear the view so that onDraw() will draw nothing but white background,
1521 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
1522 */
1523 public void clearView() {
1524 mContentWidth = 0;
1525 mContentHeight = 0;
1526 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
1527 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529 /**
1530 * Return a new picture that captures the current display of the webview.
1531 * This is a copy of the display, and will be unaffected if the webview
1532 * later loads a different URL.
1533 *
1534 * @return a picture containing the current contents of the view. Note this
1535 * picture is of the entire document, and is not restricted to the
1536 * bounds of the view.
1537 */
1538 public Picture capturePicture() {
Cary Clarkd6982c92009-05-29 11:02:22 -04001539 if (null == mWebViewCore) return null; // check for out of memory tab
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001540 return mWebViewCore.copyContentPicture();
1541 }
1542
1543 /**
1544 * Return true if the browser is displaying a TextView for text input.
1545 */
1546 private boolean inEditingMode() {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001547 return mWebTextView != null && mWebTextView.getParent() != null
1548 && mWebTextView.hasFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001549 }
1550
1551 private void clearTextEntry() {
1552 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001553 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001554 }
1555 }
1556
Cary Clarkd6982c92009-05-29 11:02:22 -04001557 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001558 * Return the current scale of the WebView
1559 * @return The current scale.
1560 */
1561 public float getScale() {
1562 return mActualScale;
1563 }
1564
1565 /**
1566 * Set the initial scale for the WebView. 0 means default. If
1567 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
1568 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
1569 * WebView starts will this value as initial scale.
1570 *
1571 * @param scaleInPercent The initial scale in percent.
1572 */
1573 public void setInitialScale(int scaleInPercent) {
1574 mInitialScale = scaleInPercent;
1575 }
1576
1577 /**
1578 * Invoke the graphical zoom picker widget for this WebView. This will
1579 * result in the zoom widget appearing on the screen to control the zoom
1580 * level of this WebView.
1581 */
1582 public void invokeZoomPicker() {
1583 if (!getSettings().supportZoom()) {
1584 Log.w(LOGTAG, "This WebView doesn't support zoom.");
1585 return;
1586 }
1587 clearTextEntry();
The Android Open Source Project10592532009-03-18 17:39:46 -07001588 if (getSettings().getBuiltInZoomControls()) {
1589 mZoomButtonsController.setVisible(true);
1590 } else {
1591 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
1592 mPrivateHandler.postDelayed(mZoomControlRunnable,
1593 ZOOM_CONTROLS_TIMEOUT);
1594 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001595 }
1596
1597 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001598 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001599 * is found and the anchor has a non-javascript url, the HitTestResult type
1600 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
1601 * anchor does not have a url or if it is a javascript url, the type will
1602 * be UNKNOWN_TYPE and the url has to be retrieved through
1603 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
1604 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
1605 * the "extra" field. A type of
1606 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
1607 * a child node. If a phone number is found, the HitTestResult type is set
1608 * to PHONE_TYPE and the phone number is set in the "extra" field of
1609 * HitTestResult. If a map address is found, the HitTestResult type is set
1610 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
1611 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
1612 * and the email is set in the "extra" field of HitTestResult. Otherwise,
1613 * HitTestResult type is set to UNKNOWN_TYPE.
1614 */
1615 public HitTestResult getHitTestResult() {
1616 if (mNativeClass == 0) {
1617 return null;
1618 }
1619
1620 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04001621 if (nativeHasCursorNode()) {
1622 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001623 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04001624 } else {
1625 String text = nativeCursorText();
1626 if (text != null) {
1627 if (text.startsWith(SCHEME_TEL)) {
1628 result.setType(HitTestResult.PHONE_TYPE);
1629 result.setExtra(text.substring(SCHEME_TEL.length()));
1630 } else if (text.startsWith(SCHEME_MAILTO)) {
1631 result.setType(HitTestResult.EMAIL_TYPE);
1632 result.setExtra(text.substring(SCHEME_MAILTO.length()));
1633 } else if (text.startsWith(SCHEME_GEO)) {
1634 result.setType(HitTestResult.GEO_TYPE);
1635 result.setExtra(URLDecoder.decode(text
1636 .substring(SCHEME_GEO.length())));
1637 } else if (nativeCursorIsAnchor()) {
1638 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
1639 result.setExtra(text);
1640 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001641 }
1642 }
1643 }
1644 int type = result.getType();
1645 if (type == HitTestResult.UNKNOWN_TYPE
1646 || type == HitTestResult.SRC_ANCHOR_TYPE) {
1647 // Now check to see if it is an image.
1648 int contentX = viewToContent((int) mLastTouchX + mScrollX);
1649 int contentY = viewToContent((int) mLastTouchY + mScrollY);
1650 String text = nativeImageURI(contentX, contentY);
1651 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001652 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
1653 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
1655 result.setExtra(text);
1656 }
1657 }
1658 return result;
1659 }
1660
1661 /**
1662 * Request the href of an anchor element due to getFocusNodePath returning
1663 * "href." If hrefMsg is null, this method returns immediately and does not
1664 * dispatch hrefMsg to its target.
Cary Clarkd6982c92009-05-29 11:02:22 -04001665 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001666 * @param hrefMsg This message will be dispatched with the result of the
1667 * request as the data member with "url" as key. The result can
1668 * be null.
1669 */
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001670 // FIXME: API change required to change the name of this function. We now
1671 // look at the cursor node, and not the focus node. Also, what is
1672 // getFocusNodePath?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001673 public void requestFocusNodeHref(Message hrefMsg) {
1674 if (hrefMsg == null || mNativeClass == 0) {
1675 return;
1676 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001677 if (nativeCursorIsAnchor()) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001678 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
Cary Clarkd6982c92009-05-29 11:02:22 -04001679 nativeCursorFramePointer(), nativeCursorNodePointer(),
1680 hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001681 }
1682 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001683
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 /**
1685 * Request the url of the image last touched by the user. msg will be sent
1686 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04001687 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001688 * @param msg This message will be dispatched with the result of the request
1689 * as the data member with "url" as key. The result can be null.
1690 */
1691 public void requestImageRef(Message msg) {
1692 int contentX = viewToContent((int) mLastTouchX + mScrollX);
1693 int contentY = viewToContent((int) mLastTouchY + mScrollY);
1694 String ref = nativeImageURI(contentX, contentY);
1695 Bundle data = msg.getData();
1696 data.putString("url", ref);
1697 msg.setData(data);
1698 msg.sendToTarget();
1699 }
1700
1701 private static int pinLoc(int x, int viewMax, int docMax) {
1702// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
1703 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001704 // pin the short document to the top/left of the screen
1705 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001706// Log.d(LOGTAG, "--- center " + x);
1707 } else if (x < 0) {
1708 x = 0;
1709// Log.d(LOGTAG, "--- zero");
1710 } else if (x + viewMax > docMax) {
1711 x = docMax - viewMax;
1712// Log.d(LOGTAG, "--- pin " + x);
1713 }
1714 return x;
1715 }
1716
1717 // Expects x in view coordinates
1718 private int pinLocX(int x) {
1719 return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
1720 }
1721
1722 // Expects y in view coordinates
1723 private int pinLocY(int y) {
1724 return pinLoc(y, getViewHeight(), computeVerticalScrollRange());
1725 }
1726
1727 /*package*/ int viewToContent(int x) {
1728 return Math.round(x * mInvActualScale);
1729 }
1730
Patrick Scott0a5ce012009-07-02 08:56:10 -04001731 /*package*/ int contentToView(int x) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001732 return Math.round(x * mActualScale);
1733 }
1734
1735 // Called by JNI to invalidate the View, given rectangle coordinates in
1736 // content space
1737 private void viewInvalidate(int l, int t, int r, int b) {
1738 invalidate(contentToView(l), contentToView(t), contentToView(r),
1739 contentToView(b));
1740 }
1741
1742 // Called by JNI to invalidate the View after a delay, given rectangle
1743 // coordinates in content space
1744 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
1745 postInvalidateDelayed(delay, contentToView(l), contentToView(t),
1746 contentToView(r), contentToView(b));
1747 }
1748
1749 private Rect contentToView(Rect x) {
1750 return new Rect(contentToView(x.left), contentToView(x.top)
1751 , contentToView(x.right), contentToView(x.bottom));
1752 }
1753
1754 /* call from webcoreview.draw(), so we're still executing in the UI thread
1755 */
1756 private void recordNewContentSize(int w, int h, boolean updateLayout) {
1757
1758 // premature data from webkit, ignore
1759 if ((w | h) == 0) {
1760 return;
1761 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001762
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 // don't abort a scroll animation if we didn't change anything
1764 if (mContentWidth != w || mContentHeight != h) {
1765 // record new dimensions
1766 mContentWidth = w;
1767 mContentHeight = h;
1768 // If history Picture is drawn, don't update scroll. They will be
1769 // updated when we get out of that mode.
1770 if (!mDrawHistory) {
1771 // repin our scroll, taking into account the new content size
1772 int oldX = mScrollX;
1773 int oldY = mScrollY;
1774 mScrollX = pinLocX(mScrollX);
1775 mScrollY = pinLocY(mScrollY);
1776 // android.util.Log.d("skia", "recordNewContentSize -
1777 // abortAnimation");
1778 mScroller.abortAnimation(); // just in case
1779 if (oldX != mScrollX || oldY != mScrollY) {
1780 sendOurVisibleRect();
1781 }
1782 }
1783 }
1784 contentSizeChanged(updateLayout);
1785 }
1786
1787 private void setNewZoomScale(float scale, boolean force) {
1788 if (scale < mMinZoomScale) {
1789 scale = mMinZoomScale;
1790 } else if (scale > mMaxZoomScale) {
1791 scale = mMaxZoomScale;
1792 }
1793 if (scale != mActualScale || force) {
1794 if (mDrawHistory) {
1795 // If history Picture is drawn, don't update scroll. They will
1796 // be updated when we get out of that mode.
1797 if (scale != mActualScale && !mPreviewZoomOnly) {
1798 mCallbackProxy.onScaleChanged(mActualScale, scale);
1799 }
1800 mActualScale = scale;
1801 mInvActualScale = 1 / scale;
1802 if (!mPreviewZoomOnly) {
1803 sendViewSizeZoom();
1804 }
1805 } else {
1806 // update our scroll so we don't appear to jump
1807 // i.e. keep the center of the doc in the center of the view
1808
1809 int oldX = mScrollX;
1810 int oldY = mScrollY;
1811 float ratio = scale * mInvActualScale; // old inverse
1812 float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
1813 float sy = ratio * oldY + (ratio - 1) * mZoomCenterY;
1814
1815 // now update our new scale and inverse
1816 if (scale != mActualScale && !mPreviewZoomOnly) {
1817 mCallbackProxy.onScaleChanged(mActualScale, scale);
1818 }
1819 mActualScale = scale;
1820 mInvActualScale = 1 / scale;
1821
Patrick Scott0a5ce012009-07-02 08:56:10 -04001822 // Scale all the child views
1823 mViewManager.scaleAll();
1824
Cary Clarkd6982c92009-05-29 11:02:22 -04001825 // as we don't have animation for scaling, don't do animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826 // for scrolling, as it causes weird intermediate state
1827 // pinScrollTo(Math.round(sx), Math.round(sy));
1828 mScrollX = pinLocX(Math.round(sx));
1829 mScrollY = pinLocY(Math.round(sy));
1830
1831 if (!mPreviewZoomOnly) {
1832 sendViewSizeZoom();
1833 sendOurVisibleRect();
1834 }
1835 }
1836 }
1837 }
1838
1839 // Used to avoid sending many visible rect messages.
1840 private Rect mLastVisibleRectSent;
1841 private Rect mLastGlobalRect;
1842
1843 private Rect sendOurVisibleRect() {
1844 Rect rect = new Rect();
1845 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001846 // Rect.equals() checks for null input.
1847 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04001848 Point pos = new Point(rect.left, rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001849 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Cary Clarked56eda2009-06-18 09:48:47 -04001850 nativeMoveGeneration(), 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001851 mLastVisibleRectSent = rect;
1852 }
1853 Rect globalRect = new Rect();
1854 if (getGlobalVisibleRect(globalRect)
1855 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04001856 if (DebugFlags.WEB_VIEW) {
1857 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
1858 + globalRect.top + ",r=" + globalRect.right + ",b="
1859 + globalRect.bottom);
1860 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001861 // TODO: the global offset is only used by windowRect()
1862 // in ChromeClientAndroid ; other clients such as touch
1863 // and mouse events could return view + screen relative points.
1864 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
1865 mLastGlobalRect = globalRect;
1866 }
1867 return rect;
1868 }
1869
1870 // Sets r to be the visible rectangle of our webview in view coordinates
1871 private void calcOurVisibleRect(Rect r) {
1872 Point p = new Point();
1873 getGlobalVisibleRect(r, p);
1874 r.offset(-p.x, -p.y);
Cary Clark3524be92009-06-22 13:09:11 -04001875 if (mFindIsUp) {
1876 r.bottom -= FIND_HEIGHT;
1877 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001878 }
1879
1880 // Sets r to be our visible rectangle in content coordinates
1881 private void calcOurContentVisibleRect(Rect r) {
1882 calcOurVisibleRect(r);
1883 r.left = viewToContent(r.left);
1884 r.top = viewToContent(r.top);
1885 r.right = viewToContent(r.right);
1886 r.bottom = viewToContent(r.bottom);
1887 }
1888
Grace Klobaef347ef2009-07-30 11:20:32 -07001889 static class ViewSizeData {
1890 int mWidth;
1891 int mHeight;
1892 int mTextWrapWidth;
1893 float mScale;
1894 }
1895
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001896 /**
1897 * Compute unzoomed width and height, and if they differ from the last
1898 * values we sent, send them to webkit (to be used has new viewport)
1899 *
1900 * @return true if new values were sent
1901 */
1902 private boolean sendViewSizeZoom() {
Grace Klobaef347ef2009-07-30 11:20:32 -07001903 int viewWidth = getViewWidth();
1904 int newWidth = Math.round(viewWidth * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001905 int newHeight = Math.round(getViewHeight() * mInvActualScale);
1906 /*
1907 * Because the native side may have already done a layout before the
1908 * View system was able to measure us, we have to send a height of 0 to
1909 * remove excess whitespace when we grow our width. This will trigger a
1910 * layout and a change in content size. This content size change will
1911 * mean that contentSizeChanged will either call this method directly or
1912 * indirectly from onSizeChanged.
1913 */
1914 if (newWidth > mLastWidthSent && mWrapContent) {
1915 newHeight = 0;
1916 }
1917 // Avoid sending another message if the dimensions have not changed.
1918 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
Grace Klobaef347ef2009-07-30 11:20:32 -07001919 ViewSizeData data = new ViewSizeData();
1920 data.mWidth = newWidth;
1921 data.mHeight = newHeight;
1922 // while in zoom overview mode, the text are wrapped to the screen
1923 // width matching mLastScale. So that we don't trigger re-flow while
1924 // toggling between overview mode and normal mode.
1925 data.mTextWrapWidth = mInZoomOverview ? Math.round(viewWidth
1926 / mLastScale) : newWidth;
1927 data.mScale = mActualScale;
1928 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001929 mLastWidthSent = newWidth;
1930 mLastHeightSent = newHeight;
1931 return true;
1932 }
1933 return false;
1934 }
1935
1936 @Override
1937 protected int computeHorizontalScrollRange() {
1938 if (mDrawHistory) {
1939 return mHistoryWidth;
1940 } else {
1941 return contentToView(mContentWidth);
1942 }
1943 }
1944
1945 // Make sure this stays in sync with the actual height of the FindDialog.
1946 private static final int FIND_HEIGHT = 79;
1947
1948 @Override
1949 protected int computeVerticalScrollRange() {
1950 if (mDrawHistory) {
1951 return mHistoryHeight;
1952 } else {
1953 int height = contentToView(mContentHeight);
1954 if (mFindIsUp) {
1955 height += FIND_HEIGHT;
1956 }
1957 return height;
1958 }
1959 }
1960
1961 /**
1962 * Get the url for the current page. This is not always the same as the url
1963 * passed to WebViewClient.onPageStarted because although the load for
1964 * that url has begun, the current page may not have changed.
1965 * @return The url for the current page.
1966 */
1967 public String getUrl() {
1968 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1969 return h != null ? h.getUrl() : null;
1970 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001971
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001972 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001973 * Get the original url for the current page. This is not always the same
1974 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001975 * load for that url has begun, the current page may not have changed.
1976 * Also, there may have been redirects resulting in a different url to that
1977 * originally requested.
1978 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001979 */
1980 public String getOriginalUrl() {
1981 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1982 return h != null ? h.getOriginalUrl() : null;
1983 }
1984
1985 /**
1986 * Get the title for the current page. This is the title of the current page
1987 * until WebViewClient.onReceivedTitle is called.
1988 * @return The title for the current page.
1989 */
1990 public String getTitle() {
1991 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1992 return h != null ? h.getTitle() : null;
1993 }
1994
1995 /**
1996 * Get the favicon for the current page. This is the favicon of the current
1997 * page until WebViewClient.onReceivedIcon is called.
1998 * @return The favicon for the current page.
1999 */
2000 public Bitmap getFavicon() {
2001 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2002 return h != null ? h.getFavicon() : null;
2003 }
2004
2005 /**
2006 * Get the progress for the current page.
2007 * @return The progress for the current page between 0 and 100.
2008 */
2009 public int getProgress() {
2010 return mCallbackProxy.getProgress();
2011 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002012
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013 /**
2014 * @return the height of the HTML content.
2015 */
2016 public int getContentHeight() {
2017 return mContentHeight;
2018 }
2019
2020 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002021 * Pause all layout, parsing, and javascript timers for all webviews. This
2022 * is a global requests, not restricted to just this webview. This can be
2023 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 */
2025 public void pauseTimers() {
2026 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
2027 }
2028
2029 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002030 * Resume all layout, parsing, and javascript timers for all webviews.
2031 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002032 */
2033 public void resumeTimers() {
2034 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
2035 }
2036
2037 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002038 * Call this to pause any extra processing associated with this view and
2039 * its associated DOM/plugins/javascript/etc. For example, if the view is
2040 * taken offscreen, this could be called to reduce unnecessary CPU and/or
2041 * network traffic. When the view is again "active", call onResume().
2042 *
2043 * Note that this differs from pauseTimers(), which affects all views/DOMs
2044 * @hide
2045 */
2046 public void onPause() {
2047 if (!mIsPaused) {
2048 mIsPaused = true;
2049 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
2050 }
2051 }
2052
2053 /**
2054 * Call this to balanace a previous call to onPause()
2055 * @hide
2056 */
2057 public void onResume() {
2058 if (mIsPaused) {
2059 mIsPaused = false;
2060 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2061 }
2062 }
2063
2064 /**
2065 * Returns true if the view is paused, meaning onPause() was called. Calling
2066 * onResume() sets the paused state back to false.
2067 * @hide
2068 */
2069 public boolean isPaused() {
2070 return mIsPaused;
2071 }
2072
2073 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002074 * Call this to inform the view that memory is low so that it can
2075 * free any available memory.
2076 * @hide
2077 */
2078 public void freeMemory() {
2079 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2080 }
2081
2082 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002083 * Clear the resource cache. Note that the cache is per-application, so
2084 * this will clear the cache for all WebViews used.
2085 *
2086 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002087 */
2088 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002089 // Note: this really needs to be a static method as it clears cache for all
2090 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2091 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002092 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2093 includeDiskFiles ? 1 : 0, 0);
2094 }
2095
2096 /**
2097 * Make sure that clearing the form data removes the adapter from the
2098 * currently focused textfield if there is one.
2099 */
2100 public void clearFormData() {
2101 if (inEditingMode()) {
2102 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002103 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002104 }
2105 }
2106
2107 /**
2108 * Tell the WebView to clear its internal back/forward list.
2109 */
2110 public void clearHistory() {
2111 mCallbackProxy.getBackForwardList().setClearPending();
2112 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2113 }
2114
2115 /**
2116 * Clear the SSL preferences table stored in response to proceeding with SSL
2117 * certificate errors.
2118 */
2119 public void clearSslPreferences() {
2120 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2121 }
2122
2123 /**
2124 * Return the WebBackForwardList for this WebView. This contains the
2125 * back/forward list for use in querying each item in the history stack.
2126 * This is a copy of the private WebBackForwardList so it contains only a
2127 * snapshot of the current state. Multiple calls to this method may return
2128 * different objects. The object returned from this method will not be
2129 * updated to reflect any new state.
2130 */
2131 public WebBackForwardList copyBackForwardList() {
2132 return mCallbackProxy.getBackForwardList().clone();
2133 }
2134
2135 /*
2136 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04002137 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002138 * calling findAll.
2139 *
2140 * @param forward Direction to search.
2141 */
2142 public void findNext(boolean forward) {
2143 nativeFindNext(forward);
2144 }
2145
2146 /*
2147 * Find all instances of find on the page and highlight them.
2148 * @param find String to find.
2149 * @return int The number of occurances of the String "find"
2150 * that were found.
2151 */
2152 public int findAll(String find) {
2153 mFindIsUp = true;
2154 int result = nativeFindAll(find.toLowerCase(), find.toUpperCase());
2155 invalidate();
2156 return result;
2157 }
2158
2159 // Used to know whether the find dialog is open. Affects whether
2160 // or not we draw the highlights for matches.
2161 private boolean mFindIsUp;
2162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002163 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002164 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002165 * location. Currently, only addresses in the United States are detected,
2166 * and consist of:
2167 * - a house number
2168 * - a street name
2169 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2170 * - a city name
2171 * - a state or territory, either spelled out or two-letter abbr.
2172 * - an optional 5 digit or 9 digit zip code.
2173 *
2174 * All names must be correctly capitalized, and the zip code, if present,
2175 * must be valid for the state. The street type must be a standard USPS
2176 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04002177 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002178 * five digits.
2179 * @param addr The string to search for addresses.
2180 *
2181 * @return the address, or if no address is found, return null.
2182 */
2183 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04002184 return findAddress(addr, false);
2185 }
2186
2187 /**
2188 * @hide
2189 * Return the first substring consisting of the address of a physical
2190 * location. Currently, only addresses in the United States are detected,
2191 * and consist of:
2192 * - a house number
2193 * - a street name
2194 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2195 * - a city name
2196 * - a state or territory, either spelled out or two-letter abbr.
2197 * - an optional 5 digit or 9 digit zip code.
2198 *
2199 * Names are optionally capitalized, and the zip code, if present,
2200 * must be valid for the state. The street type must be a standard USPS
2201 * spelling or abbreviation. The state or territory must also be spelled
2202 * or abbreviated using USPS standards. The house number may not exceed
2203 * five digits.
2204 * @param addr The string to search for addresses.
2205 * @param caseInsensitive addr Set to true to make search ignore case.
2206 *
2207 * @return the address, or if no address is found, return null.
2208 */
2209 public static String findAddress(String addr, boolean caseInsensitive) {
2210 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002211 }
2212
2213 /*
2214 * Clear the highlighting surrounding text matches created by findAll.
2215 */
2216 public void clearMatches() {
2217 mFindIsUp = false;
2218 nativeSetFindIsDown();
2219 // Now that the dialog has been removed, ensure that we scroll to a
2220 // location that is not beyond the end of the page.
2221 pinScrollTo(mScrollX, mScrollY, false, 0);
2222 invalidate();
2223 }
2224
2225 /**
2226 * Query the document to see if it contains any image references. The
2227 * message object will be dispatched with arg1 being set to 1 if images
2228 * were found and 0 if the document does not reference any images.
2229 * @param response The message that will be dispatched with the result.
2230 */
2231 public void documentHasImages(Message response) {
2232 if (response == null) {
2233 return;
2234 }
2235 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
2236 }
2237
2238 @Override
2239 public void computeScroll() {
2240 if (mScroller.computeScrollOffset()) {
2241 int oldX = mScrollX;
2242 int oldY = mScrollY;
2243 mScrollX = mScroller.getCurrX();
2244 mScrollY = mScroller.getCurrY();
2245 postInvalidate(); // So we draw again
2246 if (oldX != mScrollX || oldY != mScrollY) {
2247 // as onScrollChanged() is not called, sendOurVisibleRect()
2248 // needs to be call explicitly
2249 sendOurVisibleRect();
2250 }
2251 } else {
2252 super.computeScroll();
2253 }
2254 }
2255
2256 private static int computeDuration(int dx, int dy) {
2257 int distance = Math.max(Math.abs(dx), Math.abs(dy));
2258 int duration = distance * 1000 / STD_SPEED;
2259 return Math.min(duration, MAX_DURATION);
2260 }
2261
2262 // helper to pin the scrollBy parameters (already in view coordinates)
2263 // returns true if the scroll was changed
2264 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
2265 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
2266 }
2267
2268 // helper to pin the scrollTo parameters (already in view coordinates)
2269 // returns true if the scroll was changed
2270 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
2271 x = pinLocX(x);
2272 y = pinLocY(y);
2273 int dx = x - mScrollX;
2274 int dy = y - mScrollY;
2275
2276 if ((dx | dy) == 0) {
2277 return false;
2278 }
2279
2280 if (true && animate) {
2281 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
2282
2283 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
2284 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
2285 invalidate();
2286 } else {
2287 mScroller.abortAnimation(); // just in case
2288 scrollTo(x, y);
2289 }
2290 return true;
2291 }
2292
2293 // Scale from content to view coordinates, and pin.
2294 // Also called by jni webview.cpp
2295 private void setContentScrollBy(int cx, int cy, boolean animate) {
2296 if (mDrawHistory) {
2297 // disallow WebView to change the scroll position as History Picture
2298 // is used in the view system.
2299 // TODO: as we switchOutDrawHistory when trackball or navigation
2300 // keys are hit, this should be safe. Right?
2301 return;
2302 }
2303 cx = contentToView(cx);
2304 cy = contentToView(cy);
2305 if (mHeightCanMeasure) {
2306 // move our visible rect according to scroll request
2307 if (cy != 0) {
2308 Rect tempRect = new Rect();
2309 calcOurVisibleRect(tempRect);
2310 tempRect.offset(cx, cy);
2311 requestRectangleOnScreen(tempRect);
2312 }
2313 // FIXME: We scroll horizontally no matter what because currently
2314 // ScrollView and ListView will not scroll horizontally.
2315 // FIXME: Why do we only scroll horizontally if there is no
2316 // vertical scroll?
2317// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
2318 if (cy == 0 && cx != 0) {
2319 pinScrollBy(cx, 0, animate, 0);
2320 }
2321 } else {
2322 pinScrollBy(cx, cy, animate, 0);
2323 }
2324 }
2325
2326 // scale from content to view coordinates, and pin
2327 // return true if pin caused the final x/y different than the request cx/cy;
2328 // return false if the view scroll to the exact position as it is requested.
2329 private boolean setContentScrollTo(int cx, int cy) {
2330 if (mDrawHistory) {
2331 // disallow WebView to change the scroll position as History Picture
2332 // is used in the view system.
2333 // One known case where this is called is that WebCore tries to
2334 // restore the scroll position. As history Picture already uses the
2335 // saved scroll position, it is ok to skip this.
2336 return false;
2337 }
2338 int vx = contentToView(cx);
2339 int vy = contentToView(cy);
2340// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
2341// vx + " " + vy + "]");
2342 pinScrollTo(vx, vy, false, 0);
2343 if (mScrollX != vx || mScrollY != vy) {
2344 return true;
2345 } else {
2346 return false;
2347 }
2348 }
2349
2350 // scale from content to view coordinates, and pin
2351 private void spawnContentScrollTo(int cx, int cy) {
2352 if (mDrawHistory) {
2353 // disallow WebView to change the scroll position as History Picture
2354 // is used in the view system.
2355 return;
2356 }
2357 int vx = contentToView(cx);
2358 int vy = contentToView(cy);
2359 pinScrollTo(vx, vy, true, 0);
2360 }
2361
2362 /**
2363 * These are from webkit, and are in content coordinate system (unzoomed)
2364 */
2365 private void contentSizeChanged(boolean updateLayout) {
2366 // suppress 0,0 since we usually see real dimensions soon after
2367 // this avoids drawing the prev content in a funny place. If we find a
2368 // way to consolidate these notifications, this check may become
2369 // obsolete
2370 if ((mContentWidth | mContentHeight) == 0) {
2371 return;
2372 }
2373
2374 if (mHeightCanMeasure) {
2375 if (getMeasuredHeight() != contentToView(mContentHeight)
2376 && updateLayout) {
2377 requestLayout();
2378 }
2379 } else if (mWidthCanMeasure) {
2380 if (getMeasuredWidth() != contentToView(mContentWidth)
2381 && updateLayout) {
2382 requestLayout();
2383 }
2384 } else {
2385 // If we don't request a layout, try to send our view size to the
2386 // native side to ensure that WebCore has the correct dimensions.
2387 sendViewSizeZoom();
2388 }
2389 }
2390
2391 /**
2392 * Set the WebViewClient that will receive various notifications and
2393 * requests. This will replace the current handler.
2394 * @param client An implementation of WebViewClient.
2395 */
2396 public void setWebViewClient(WebViewClient client) {
2397 mCallbackProxy.setWebViewClient(client);
2398 }
2399
2400 /**
2401 * Register the interface to be used when content can not be handled by
2402 * the rendering engine, and should be downloaded instead. This will replace
2403 * the current handler.
2404 * @param listener An implementation of DownloadListener.
2405 */
2406 public void setDownloadListener(DownloadListener listener) {
2407 mCallbackProxy.setDownloadListener(listener);
2408 }
2409
2410 /**
2411 * Set the chrome handler. This is an implementation of WebChromeClient for
2412 * use in handling Javascript dialogs, favicons, titles, and the progress.
2413 * This will replace the current handler.
2414 * @param client An implementation of WebChromeClient.
2415 */
2416 public void setWebChromeClient(WebChromeClient client) {
2417 mCallbackProxy.setWebChromeClient(client);
2418 }
2419
2420 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01002421 * Gets the chrome handler.
2422 * @return the current WebChromeClient instance.
2423 *
2424 * @hide API council approval.
2425 */
2426 public WebChromeClient getWebChromeClient() {
2427 return mCallbackProxy.getWebChromeClient();
2428 }
2429
2430 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002431 * Set the Picture listener. This is an interface used to receive
2432 * notifications of a new Picture.
2433 * @param listener An implementation of WebView.PictureListener.
2434 */
2435 public void setPictureListener(PictureListener listener) {
2436 mPictureListener = listener;
2437 }
2438
2439 /**
2440 * {@hide}
2441 */
2442 /* FIXME: Debug only! Remove for SDK! */
2443 public void externalRepresentation(Message callback) {
2444 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
2445 }
2446
2447 /**
2448 * {@hide}
2449 */
2450 /* FIXME: Debug only! Remove for SDK! */
2451 public void documentAsText(Message callback) {
2452 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
2453 }
2454
2455 /**
2456 * Use this function to bind an object to Javascript so that the
2457 * methods can be accessed from Javascript.
2458 * <p><strong>IMPORTANT:</strong>
2459 * <ul>
2460 * <li> Using addJavascriptInterface() allows JavaScript to control your
2461 * application. This can be a very useful feature or a dangerous security
2462 * issue. When the HTML in the WebView is untrustworthy (for example, part
2463 * or all of the HTML is provided by some person or process), then an
2464 * attacker could inject HTML that will execute your code and possibly any
2465 * code of the attacker's choosing.<br>
2466 * Do not use addJavascriptInterface() unless all of the HTML in this
2467 * WebView was written by you.</li>
2468 * <li> The Java object that is bound runs in another thread and not in
2469 * the thread that it was constructed in.</li>
2470 * </ul></p>
2471 * @param obj The class instance to bind to Javascript
2472 * @param interfaceName The name to used to expose the class in Javascript
2473 */
2474 public void addJavascriptInterface(Object obj, String interfaceName) {
Cary Clarkded054c2009-06-15 10:26:08 -04002475 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
2476 arg.mObject = obj;
2477 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002478 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
2479 }
2480
2481 /**
2482 * Return the WebSettings object used to control the settings for this
2483 * WebView.
2484 * @return A WebSettings object that can be used to control this WebView's
2485 * settings.
2486 */
2487 public WebSettings getSettings() {
2488 return mWebViewCore.getSettings();
2489 }
2490
2491 /**
2492 * Return the list of currently loaded plugins.
2493 * @return The list of currently loaded plugins.
2494 */
2495 public static synchronized PluginList getPluginList() {
2496 if (sPluginList == null) {
2497 sPluginList = new PluginList();
2498 }
2499 return sPluginList;
2500 }
2501
2502 /**
Grace Kloba658ab7d2009-05-14 14:45:26 -07002503 * TODO: need to add @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002504 */
2505 public void refreshPlugins(boolean reloadOpenPages) {
Grace Kloba658ab7d2009-05-14 14:45:26 -07002506 PluginManager.getInstance(mContext).refreshPlugins(reloadOpenPages);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002507 }
2508
2509 //-------------------------------------------------------------------------
2510 // Override View methods
2511 //-------------------------------------------------------------------------
2512
2513 @Override
2514 protected void finalize() throws Throwable {
2515 destroy();
2516 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002517
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002518 @Override
2519 protected void onDraw(Canvas canvas) {
2520 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
2521 if (mNativeClass == 0) {
2522 return;
2523 }
2524 if (mWebViewCore.mEndScaleZoom) {
2525 mWebViewCore.mEndScaleZoom = false;
Cary Clarkd6982c92009-05-29 11:02:22 -04002526 if (mTouchMode >= FIRST_SCROLL_ZOOM
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002527 && mTouchMode <= LAST_SCROLL_ZOOM) {
2528 setHorizontalScrollBarEnabled(true);
2529 setVerticalScrollBarEnabled(true);
2530 mTouchMode = TOUCH_DONE_MODE;
2531 }
2532 }
2533 int sc = canvas.save();
2534 if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
2535 scrollZoomDraw(canvas);
2536 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002537 // Update the buttons in the picture, so when we draw the picture
2538 // to the screen, they are in the correct state.
2539 // Tell the native side if user is a) touching the screen,
2540 // b) pressing the trackball down, or c) pressing the enter key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002541 // 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 -08002542 // state.
2543 // If mNativeClass is 0, we should not reach here, so we do not
2544 // need to check it again.
2545 nativeRecordButtons(hasFocus() && hasWindowFocus(),
2546 mTouchMode == TOUCH_SHORTPRESS_START_MODE
Leon Scrogginse3225672009-06-03 15:53:13 -04002547 || mTrackballDown || mGotCenterDown, false);
Cary Clarkd6982c92009-05-29 11:02:22 -04002548 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002549 }
2550 canvas.restoreToCount(sc);
Cary Clarkd6982c92009-05-29 11:02:22 -04002551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002552 if (AUTO_REDRAW_HACK && mAutoRedraw) {
2553 invalidate();
2554 }
2555 }
2556
2557 @Override
2558 public void setLayoutParams(ViewGroup.LayoutParams params) {
2559 if (params.height == LayoutParams.WRAP_CONTENT) {
2560 mWrapContent = true;
2561 }
2562 super.setLayoutParams(params);
2563 }
2564
2565 @Override
2566 public boolean performLongClick() {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04002567 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
2568 // Send the click so that the textfield is in focus
2569 // FIXME: When we start respecting changes to the native textfield's
2570 // selection, need to make sure that this does not change it.
2571 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
2572 nativeCursorNodePointer());
2573 rebuildWebTextView();
2574 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002575 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002576 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002577 } else {
2578 return super.performLongClick();
2579 }
2580 }
2581
Cary Clarkd6982c92009-05-29 11:02:22 -04002582 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002583 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002584 if (mDrawHistory) {
2585 canvas.scale(mActualScale, mActualScale);
2586 canvas.drawPicture(mHistoryPicture);
2587 return;
2588 }
2589
2590 boolean animateZoom = mZoomScale != 0;
Cary Clarkd6982c92009-05-29 11:02:22 -04002591 boolean animateScroll = !mScroller.isFinished()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002592 || mVelocityTracker != null;
2593 if (animateZoom) {
2594 float zoomScale;
2595 int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
2596 if (interval < ZOOM_ANIMATION_LENGTH) {
2597 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
Cary Clarkd6982c92009-05-29 11:02:22 -04002598 zoomScale = 1.0f / (mInvInitialZoomScale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002599 + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
2600 invalidate();
2601 } else {
2602 zoomScale = mZoomScale;
2603 // set mZoomScale to be 0 as we have done animation
2604 mZoomScale = 0;
2605 }
Grace Kloba675c7d22009-07-23 09:21:21 -07002606 float scale = zoomScale * mInvInitialZoomScale;
2607 int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
2608 - mZoomCenterX);
2609 tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
2610 * zoomScale)) + mScrollX;
2611 int ty = Math.round(scale * (mInitialScrollY + mZoomCenterY)
2612 - mZoomCenterY);
2613 ty = -pinLoc(ty, getViewHeight(), Math.round(mContentHeight
2614 * zoomScale)) + mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002615 canvas.translate(tx, ty);
2616 canvas.scale(zoomScale, zoomScale);
2617 } else {
2618 canvas.scale(mActualScale, mActualScale);
2619 }
2620
2621 mWebViewCore.drawContentPicture(canvas, color, animateZoom,
2622 animateScroll);
2623
2624 if (mNativeClass == 0) return;
2625 if (mShiftIsPressed) {
2626 if (mTouchSelection) {
2627 nativeDrawSelectionRegion(canvas);
2628 } else {
Cary Clarkd6982c92009-05-29 11:02:22 -04002629 nativeDrawSelection(canvas, mSelectX, mSelectY,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002630 mExtendSelection);
2631 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002632 } else if (drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002633 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
2634 mTouchMode = TOUCH_SHORTPRESS_MODE;
2635 HitTestResult hitTest = getHitTestResult();
2636 if (hitTest != null &&
2637 hitTest.mType != HitTestResult.UNKNOWN_TYPE) {
2638 mPrivateHandler.sendMessageDelayed(mPrivateHandler
2639 .obtainMessage(SWITCH_TO_LONGPRESS),
2640 LONG_PRESS_TIMEOUT);
2641 }
2642 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002643 nativeDrawCursorRing(canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002644 }
2645 // When the FindDialog is up, only draw the matches if we are not in
2646 // the process of scrolling them into view.
2647 if (mFindIsUp && !animateScroll) {
2648 nativeDrawMatches(canvas);
2649 }
2650 }
2651
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002652 private float scrollZoomGridScale(float invScale) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002653 float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002654 / (float) SCROLL_ZOOM_GRID;
2655 return 1.0f / griddedInvScale;
2656 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002657
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002658 private float scrollZoomX(float scale) {
2659 int width = getViewWidth();
2660 float maxScrollZoomX = mContentWidth * scale - width;
2661 int maxX = mContentWidth - width;
2662 return -(maxScrollZoomX > 0 ? mZoomScrollX * maxScrollZoomX / maxX
2663 : maxScrollZoomX / 2);
2664 }
2665
2666 private float scrollZoomY(float scale) {
2667 int height = getViewHeight();
2668 float maxScrollZoomY = mContentHeight * scale - height;
2669 int maxY = mContentHeight - height;
2670 return -(maxScrollZoomY > 0 ? mZoomScrollY * maxScrollZoomY / maxY
2671 : maxScrollZoomY / 2);
2672 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002673
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002674 private void drawMagnifyFrame(Canvas canvas, Rect frame, Paint paint) {
2675 final float ADORNMENT_LEN = 16.0f;
2676 float width = frame.width();
2677 float height = frame.height();
2678 Path path = new Path();
2679 path.moveTo(-ADORNMENT_LEN, -ADORNMENT_LEN);
2680 path.lineTo(0, 0);
2681 path.lineTo(width, 0);
2682 path.lineTo(width + ADORNMENT_LEN, -ADORNMENT_LEN);
2683 path.moveTo(-ADORNMENT_LEN, height + ADORNMENT_LEN);
2684 path.lineTo(0, height);
2685 path.lineTo(width, height);
2686 path.lineTo(width + ADORNMENT_LEN, height + ADORNMENT_LEN);
2687 path.moveTo(0, 0);
2688 path.lineTo(0, height);
2689 path.moveTo(width, 0);
2690 path.lineTo(width, height);
2691 path.offset(frame.left, frame.top);
2692 canvas.drawPath(path, paint);
2693 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002694
2695 // Returns frame surrounding magified portion of screen while
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002696 // scroll-zoom is enabled. The frame is also used to center the
2697 // zoom-in zoom-out points at the start and end of the animation.
2698 private Rect scrollZoomFrame(int width, int height, float halfScale) {
2699 Rect scrollFrame = new Rect();
Cary Clarkd6982c92009-05-29 11:02:22 -04002700 scrollFrame.set(mZoomScrollX, mZoomScrollY,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002701 mZoomScrollX + width, mZoomScrollY + height);
2702 if (mContentWidth * mZoomScrollLimit < width) {
2703 float scale = zoomFrameScaleX(width, halfScale, 1.0f);
2704 float offsetX = (width * scale - width) * 0.5f;
2705 scrollFrame.left -= offsetX;
2706 scrollFrame.right += offsetX;
2707 }
2708 if (mContentHeight * mZoomScrollLimit < height) {
2709 float scale = zoomFrameScaleY(height, halfScale, 1.0f);
2710 float offsetY = (height * scale - height) * 0.5f;
2711 scrollFrame.top -= offsetY;
2712 scrollFrame.bottom += offsetY;
2713 }
2714 return scrollFrame;
2715 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002716
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002717 private float zoomFrameScaleX(int width, float halfScale, float noScale) {
2718 // mContentWidth > width > mContentWidth * mZoomScrollLimit
2719 if (mContentWidth <= width) {
2720 return halfScale;
2721 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002722 float part = (width - mContentWidth * mZoomScrollLimit)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002723 / (width * (1 - mZoomScrollLimit));
2724 return halfScale * part + noScale * (1.0f - part);
2725 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002726
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002727 private float zoomFrameScaleY(int height, float halfScale, float noScale) {
2728 if (mContentHeight <= height) {
2729 return halfScale;
2730 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002731 float part = (height - mContentHeight * mZoomScrollLimit)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002732 / (height * (1 - mZoomScrollLimit));
2733 return halfScale * part + noScale * (1.0f - part);
2734 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002735
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002736 private float scrollZoomMagScale(float invScale) {
2737 return (invScale * 2 + mInvActualScale) / 3;
2738 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002739
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002740 private void scrollZoomDraw(Canvas canvas) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002741 float invScale = mZoomScrollInvLimit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002742 int elapsed = 0;
2743 if (mTouchMode != SCROLL_ZOOM_OUT) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002744 elapsed = (int) Math.min(System.currentTimeMillis()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002745 - mZoomScrollStart, SCROLL_ZOOM_DURATION);
Cary Clarkd6982c92009-05-29 11:02:22 -04002746 float transitionScale = (mZoomScrollInvLimit - mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002747 * elapsed / SCROLL_ZOOM_DURATION;
2748 if (mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
2749 invScale = mInvActualScale + transitionScale;
2750 } else { /* if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) */
2751 invScale = mZoomScrollInvLimit - transitionScale;
2752 }
2753 }
2754 float scale = scrollZoomGridScale(invScale);
2755 invScale = 1.0f / scale;
2756 int width = getViewWidth();
2757 int height = getViewHeight();
2758 float halfScale = scrollZoomMagScale(invScale);
2759 Rect scrollFrame = scrollZoomFrame(width, height, halfScale);
2760 if (elapsed == SCROLL_ZOOM_DURATION) {
2761 if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
2762 setHorizontalScrollBarEnabled(true);
2763 setVerticalScrollBarEnabled(true);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002764 rebuildWebTextView();
Cary Clarkd6982c92009-05-29 11:02:22 -04002765 scrollTo((int) (scrollFrame.centerX() * mActualScale)
2766 - (width >> 1), (int) (scrollFrame.centerY()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002767 * mActualScale) - (height >> 1));
2768 mTouchMode = TOUCH_DONE_MODE;
Patrick Scott0a5ce012009-07-02 08:56:10 -04002769 // Show all the child views once we are done.
2770 mViewManager.showAll();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002771 } else {
2772 mTouchMode = SCROLL_ZOOM_OUT;
2773 }
2774 }
2775 float newX = scrollZoomX(scale);
2776 float newY = scrollZoomY(scale);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002777 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002778 Log.v(LOGTAG, "scrollZoomDraw scale=" + scale + " + (" + newX
2779 + ", " + newY + ") mZoomScroll=(" + mZoomScrollX + ", "
Cary Clarkd6982c92009-05-29 11:02:22 -04002780 + mZoomScrollY + ")" + " invScale=" + invScale + " scale="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002781 + scale);
2782 }
2783 canvas.translate(newX, newY);
2784 canvas.scale(scale, scale);
2785 boolean animating = mTouchMode != SCROLL_ZOOM_OUT;
2786 if (mDrawHistory) {
2787 int sc = canvas.save(Canvas.CLIP_SAVE_FLAG);
2788 Rect clip = new Rect(0, 0, mHistoryPicture.getWidth(),
2789 mHistoryPicture.getHeight());
2790 canvas.clipRect(clip, Region.Op.DIFFERENCE);
2791 canvas.drawColor(mBackgroundColor);
2792 canvas.restoreToCount(sc);
2793 canvas.drawPicture(mHistoryPicture);
2794 } else {
2795 mWebViewCore.drawContentPicture(canvas, mBackgroundColor,
2796 animating, true);
2797 }
2798 if (mTouchMode == TOUCH_DONE_MODE) {
2799 return;
2800 }
2801 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
2802 paint.setStyle(Paint.Style.STROKE);
2803 paint.setStrokeWidth(30.0f);
2804 paint.setARGB(0x50, 0, 0, 0);
2805 int maxX = mContentWidth - width;
2806 int maxY = mContentHeight - height;
2807 if (true) { // experiment: draw hint to place finger off magnify area
2808 drawMagnifyFrame(canvas, scrollFrame, paint);
2809 } else {
2810 canvas.drawRect(scrollFrame, paint);
2811 }
2812 int sc = canvas.save();
2813 canvas.clipRect(scrollFrame);
2814 float halfX = (float) mZoomScrollX / maxX;
2815 if (mContentWidth * mZoomScrollLimit < width) {
2816 halfX = zoomFrameScaleX(width, 0.5f, halfX);
2817 }
2818 float halfY = (float) mZoomScrollY / maxY;
2819 if (mContentHeight * mZoomScrollLimit < height) {
2820 halfY = zoomFrameScaleY(height, 0.5f, halfY);
2821 }
2822 canvas.scale(halfScale, halfScale, mZoomScrollX + width * halfX
2823 , mZoomScrollY + height * halfY);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002824 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002825 Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=("
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002826 + width + ", " + height + ") half=(" + halfX + ", "
2827 + halfY + ")");
2828 }
2829 if (mDrawHistory) {
2830 canvas.drawPicture(mHistoryPicture);
2831 } else {
2832 mWebViewCore.drawContentPicture(canvas, mBackgroundColor,
2833 animating, false);
2834 }
2835 canvas.restoreToCount(sc);
2836 if (mTouchMode != SCROLL_ZOOM_OUT) {
2837 invalidate();
2838 }
2839 }
2840
2841 private void zoomScrollTap(float x, float y) {
2842 float scale = scrollZoomGridScale(mZoomScrollInvLimit);
2843 float left = scrollZoomX(scale);
2844 float top = scrollZoomY(scale);
2845 int width = getViewWidth();
2846 int height = getViewHeight();
2847 x -= width * scale / 2;
2848 y -= height * scale / 2;
2849 mZoomScrollX = Math.min(mContentWidth - width
2850 , Math.max(0, (int) ((x - left) / scale)));
2851 mZoomScrollY = Math.min(mContentHeight - height
2852 , Math.max(0, (int) ((y - top) / scale)));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002853 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002854 Log.v(LOGTAG, "zoomScrollTap scale=" + scale + " + (" + left
2855 + ", " + top + ") mZoomScroll=(" + mZoomScrollX + ", "
2856 + mZoomScrollY + ")" + " x=" + x + " y=" + y);
2857 }
2858 }
2859
Leon Scrogginsa5645b22009-06-09 15:31:19 -04002860 /**
2861 * @hide
2862 */
2863 public boolean canZoomScrollOut() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002864 if (mContentWidth == 0 || mContentHeight == 0) {
2865 return false;
2866 }
2867 int width = getViewWidth();
2868 int height = getViewHeight();
2869 float x = (float) width / (float) mContentWidth;
2870 float y = (float) height / (float) mContentHeight;
2871 mZoomScrollLimit = Math.max(DEFAULT_MIN_ZOOM_SCALE, Math.min(x, y));
2872 mZoomScrollInvLimit = 1.0f / mZoomScrollLimit;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002873 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002874 Log.v(LOGTAG, "canZoomScrollOut"
2875 + " mInvActualScale=" + mInvActualScale
2876 + " mZoomScrollLimit=" + mZoomScrollLimit
2877 + " mZoomScrollInvLimit=" + mZoomScrollInvLimit
2878 + " mContentWidth=" + mContentWidth
2879 + " mContentHeight=" + mContentHeight
2880 );
2881 }
2882 // don't zoom out unless magnify area is at least half as wide
2883 // or tall as content
2884 float limit = mZoomScrollLimit * 2;
2885 return mContentWidth >= width * limit
2886 || mContentHeight >= height * limit;
2887 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002888
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002889 private void startZoomScrollOut() {
2890 setHorizontalScrollBarEnabled(false);
2891 setVerticalScrollBarEnabled(false);
The Android Open Source Project10592532009-03-18 17:39:46 -07002892 if (getSettings().getBuiltInZoomControls()) {
2893 if (mZoomButtonsController.isVisible()) {
2894 mZoomButtonsController.setVisible(false);
2895 }
2896 } else {
2897 if (mZoomControlRunnable != null) {
2898 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
2899 }
2900 if (mZoomControls != null) {
2901 mZoomControls.hide();
2902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002903 }
2904 int width = getViewWidth();
2905 int height = getViewHeight();
2906 int halfW = width >> 1;
2907 mLastTouchX = halfW;
2908 int halfH = height >> 1;
2909 mLastTouchY = halfH;
2910 mScroller.abortAnimation();
2911 mZoomScrollStart = System.currentTimeMillis();
2912 Rect zoomFrame = scrollZoomFrame(width, height
2913 , scrollZoomMagScale(mZoomScrollInvLimit));
Cary Clarkd6982c92009-05-29 11:02:22 -04002914 mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002915 - (zoomFrame.width() >> 1));
Cary Clarkd6982c92009-05-29 11:02:22 -04002916 mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002917 - (zoomFrame.height() >> 1));
2918 scrollTo(0, 0); // triggers inval, starts animation
2919 clearTextEntry();
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002920 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002921 Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=("
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002922 + mZoomScrollX + ", " + mZoomScrollY +")");
2923 }
2924 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002925
Leon Scrogginsa5645b22009-06-09 15:31:19 -04002926 /**
2927 * @hide
2928 */
2929 public void zoomScrollOut() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002930 if (canZoomScrollOut() == false) {
2931 mTouchMode = TOUCH_DONE_MODE;
2932 return;
2933 }
Patrick Scott0a5ce012009-07-02 08:56:10 -04002934 // Hide the child views while in this mode.
2935 mViewManager.hideAll();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002936 startZoomScrollOut();
2937 mTouchMode = SCROLL_ZOOM_ANIMATION_OUT;
2938 invalidate();
2939 }
2940
2941 private void moveZoomScrollWindow(float x, float y) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002942 if (Math.abs(x - mLastZoomScrollRawX) < 1.5f
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002943 && Math.abs(y - mLastZoomScrollRawY) < 1.5f) {
2944 return;
2945 }
2946 mLastZoomScrollRawX = x;
2947 mLastZoomScrollRawY = y;
2948 int oldX = mZoomScrollX;
2949 int oldY = mZoomScrollY;
2950 int width = getViewWidth();
2951 int height = getViewHeight();
2952 int maxZoomX = mContentWidth - width;
2953 if (maxZoomX > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002954 int maxScreenX = width - (int) Math.ceil(width
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002955 * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002956 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002957 Log.v(LOGTAG, "moveZoomScrollWindow-X"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002958 + " maxScreenX=" + maxScreenX + " width=" + width
Cary Clarkd6982c92009-05-29 11:02:22 -04002959 + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002960 }
2961 x += maxScreenX * mLastScrollX / maxZoomX - mLastTouchX;
2962 x *= Math.max(maxZoomX / maxScreenX, mZoomScrollInvLimit);
2963 mZoomScrollX = Math.max(0, Math.min(maxZoomX, (int) x));
2964 }
2965 int maxZoomY = mContentHeight - height;
2966 if (maxZoomY > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002967 int maxScreenY = height - (int) Math.ceil(height
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002968 * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002969 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002970 Log.v(LOGTAG, "moveZoomScrollWindow-Y"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002971 + " maxScreenY=" + maxScreenY + " height=" + height
Cary Clarkd6982c92009-05-29 11:02:22 -04002972 + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002973 }
2974 y += maxScreenY * mLastScrollY / maxZoomY - mLastTouchY;
2975 y *= Math.max(maxZoomY / maxScreenY, mZoomScrollInvLimit);
2976 mZoomScrollY = Math.max(0, Math.min(maxZoomY, (int) y));
2977 }
2978 if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
2979 invalidate();
2980 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002981 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002982 Log.v(LOGTAG, "moveZoomScrollWindow"
2983 + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")"
2984 + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")"
2985 + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")"
2986 + " last=("+mLastScrollX+", "+mLastScrollY+")"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002987 + " x=" + x + " y=" + y);
2988 }
2989 }
2990
2991 private void setZoomScrollIn() {
2992 mZoomScrollStart = System.currentTimeMillis();
2993 }
2994
2995 private float mZoomScrollLimit;
2996 private float mZoomScrollInvLimit;
2997 private int mLastScrollX;
2998 private int mLastScrollY;
2999 private long mZoomScrollStart;
3000 private int mZoomScrollX;
3001 private int mZoomScrollY;
3002 private float mLastZoomScrollRawX = -1000.0f;
3003 private float mLastZoomScrollRawY = -1000.0f;
3004 // The zoomed scale varies from 1.0 to DEFAULT_MIN_ZOOM_SCALE == 0.25.
3005 // The zoom animation duration SCROLL_ZOOM_DURATION == 0.5.
3006 // Two pressures compete for gridding; a high frame rate (e.g. 20 fps)
3007 // and minimizing font cache allocations (fewer frames is better).
3008 // A SCROLL_ZOOM_GRID of 6 permits about 20 zoom levels over 0.5 seconds:
3009 // the inverse of: 1.0, 1.16, 1.33, 1.5, 1.67, 1.84, 2.0, etc. to 4.0
3010 private static final int SCROLL_ZOOM_GRID = 6;
3011 private static final int SCROLL_ZOOM_DURATION = 500;
3012 // Make it easier to get to the bottom of a document by reserving a 32
3013 // pixel buffer, for when the starting drag is a bit below the bottom of
3014 // the magnify frame.
3015 private static final int SCROLL_ZOOM_FINGER_BUFFER = 32;
3016
3017 // draw history
3018 private boolean mDrawHistory = false;
3019 private Picture mHistoryPicture = null;
3020 private int mHistoryWidth = 0;
3021 private int mHistoryHeight = 0;
3022
3023 // Only check the flag, can be called from WebCore thread
3024 boolean drawHistory() {
3025 return mDrawHistory;
3026 }
3027
3028 // Should only be called in UI thread
3029 void switchOutDrawHistory() {
3030 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Cary Clarkbc2e33b2009-04-23 13:12:19 -04003031 if (mDrawHistory && mWebViewCore.pictureReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003032 mDrawHistory = false;
3033 invalidate();
3034 int oldScrollX = mScrollX;
3035 int oldScrollY = mScrollY;
3036 mScrollX = pinLocX(mScrollX);
3037 mScrollY = pinLocY(mScrollY);
3038 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
3039 mUserScroll = false;
3040 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
3041 oldScrollY);
3042 }
3043 sendOurVisibleRect();
3044 }
3045 }
3046
Cary Clarkd6982c92009-05-29 11:02:22 -04003047 WebViewCore.CursorData cursorData() {
3048 WebViewCore.CursorData result = new WebViewCore.CursorData();
3049 result.mMoveGeneration = nativeMoveGeneration();
3050 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04003051 Point position = nativeCursorPosition();
3052 result.mX = position.x;
3053 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04003054 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003055 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003056
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003057 /**
3058 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04003059 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003060 * order, swap them.
3061 * @param start Beginning of selection to delete.
3062 * @param end End of selection to delete.
3063 */
3064 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003065 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04003066 WebViewCore.DeleteSelectionData data
3067 = new WebViewCore.DeleteSelectionData();
3068 data.mStart = start;
3069 data.mEnd = end;
3070 data.mTextGeneration = mTextGeneration;
3071 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, 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 */
3126 private 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
Cary Clark19436562009-06-04 16:25:07 -04003388 mWebViewCore.sendMessage(EventHub.CLICK);
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003389 // This will bring up the WebTextView and put it in focus, for
3390 // our view system's notion of focus
3391 rebuildWebTextView();
3392 // Now we need to pass the event to it
3393 return mWebTextView.onKeyDown(keyCode, event);
Leon Scroggins40981262009-07-01 10:57:47 -04003394 } else if (nativeHasFocusNode()) {
3395 // In this case, the cursor is not on a text input, but the focus
3396 // might be. Check it, and if so, hand over to the WebTextView.
3397 rebuildWebTextView();
3398 if (inEditingMode()) {
3399 return mWebTextView.onKeyDown(keyCode, event);
3400 }
Cary Clark19436562009-06-04 16:25:07 -04003401 }
3402
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003403 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003404 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003405 // pass the key to DOM
3406 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
3407 // return true as DOM handles the key
3408 return true;
3409 }
3410
3411 // Bubble up the key event as WebView doesn't handle it
3412 return false;
3413 }
3414
3415 @Override
3416 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003417 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003418 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003419 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003420 }
3421
3422 if (mNativeClass == 0) {
3423 return false;
3424 }
3425
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003426 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04003427 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
3428 String text = nativeCursorText();
3429 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003430 && text.startsWith(SCHEME_TEL)) {
3431 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
3432 getContext().startActivity(intent);
3433 return true;
3434 }
3435 }
3436
3437 // Bubble up the key event if
3438 // 1. it is a system key; or
3439 // 2. the host application wants to handle it;
3440 if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
3441 return false;
3442 }
3443
3444 // special handling in scroll_zoom state
3445 if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
3446 if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode
3447 && mTouchMode != SCROLL_ZOOM_ANIMATION_IN) {
3448 setZoomScrollIn();
3449 mTouchMode = SCROLL_ZOOM_ANIMATION_IN;
3450 invalidate();
3451 return true;
3452 }
3453 return false;
3454 }
3455
Cary Clarkd6982c92009-05-29 11:02:22 -04003456 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003457 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
3458 if (commitCopy()) {
3459 return true;
3460 }
3461 }
3462
3463 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3464 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3465 // always handle the navigation keys in the UI thread
3466 // Bubble up the key event as WebView doesn't handle it
3467 return false;
3468 }
3469
Leon Scrogginse3225672009-06-03 15:53:13 -04003470 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003471 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04003472 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
3473 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003474
Leon Scrogginse3225672009-06-03 15:53:13 -04003475 if (mShiftIsPressed) {
3476 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003477 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003478 if (getSettings().supportZoom()
3479 && mTouchMode == TOUCH_DOUBLECLICK_MODE) {
3480 zoomScrollOut();
3481 } else {
3482 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3483 .obtainMessage(SWITCH_TO_CLICK), TAP_TIMEOUT);
3484 if (DebugFlags.WEB_VIEW) {
3485 Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
3486 }
3487 mTouchMode = TOUCH_DOUBLECLICK_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003488 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003489 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003490 }
3491
3492 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003493 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003494 // pass the key to DOM
3495 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
3496 // return true as DOM handles the key
3497 return true;
3498 }
3499
3500 // Bubble up the key event as WebView doesn't handle it
3501 return false;
3502 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003503
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003504 /**
3505 * @hide
3506 */
3507 public void emulateShiftHeld() {
3508 mExtendSelection = false;
3509 mShiftIsPressed = true;
Cary Clarke872f3a2009-06-11 09:51:11 -04003510 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003511 }
3512
3513 private boolean commitCopy() {
3514 boolean copiedSomething = false;
3515 if (mExtendSelection) {
3516 // copy region so core operates on copy without touching orig.
3517 Region selection = new Region(nativeGetSelection());
3518 if (selection.isEmpty() == false) {
3519 Toast.makeText(mContext
3520 , com.android.internal.R.string.text_copied
3521 , Toast.LENGTH_SHORT).show();
3522 mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection);
3523 copiedSomething = true;
3524 }
3525 mExtendSelection = false;
3526 }
3527 mShiftIsPressed = false;
3528 if (mTouchMode == TOUCH_SELECT_MODE) {
3529 mTouchMode = TOUCH_INIT_MODE;
3530 }
3531 return copiedSomething;
3532 }
3533
3534 // Set this as a hierarchy change listener so we can know when this view
3535 // is removed and still have access to our parent.
3536 @Override
3537 protected void onAttachedToWindow() {
3538 super.onAttachedToWindow();
3539 ViewParent parent = getParent();
3540 if (parent instanceof ViewGroup) {
3541 ViewGroup p = (ViewGroup) parent;
3542 p.setOnHierarchyChangeListener(this);
3543 }
3544 }
3545
3546 @Override
3547 protected void onDetachedFromWindow() {
3548 super.onDetachedFromWindow();
3549 ViewParent parent = getParent();
3550 if (parent instanceof ViewGroup) {
3551 ViewGroup p = (ViewGroup) parent;
3552 p.setOnHierarchyChangeListener(null);
3553 }
3554
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003555 // Clean up the zoom controller
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003556 mZoomButtonsController.setVisible(false);
3557 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003558
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003559 // Implementation for OnHierarchyChangeListener
3560 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04003561
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003562 public void onChildViewRemoved(View p, View child) {
3563 if (child == this) {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003564 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003565 }
3566 }
3567
3568 /**
3569 * @deprecated WebView should not have implemented
3570 * ViewTreeObserver.OnGlobalFocusChangeListener. This method
3571 * does nothing now.
3572 */
3573 @Deprecated
3574 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
3575 }
3576
Cary Clarkd6982c92009-05-29 11:02:22 -04003577 // To avoid drawing the cursor ring, and remove the TextView when our window
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003578 // loses focus.
3579 @Override
3580 public void onWindowFocusChanged(boolean hasWindowFocus) {
3581 if (hasWindowFocus) {
3582 if (hasFocus()) {
3583 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003584 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04003585 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003586 if (mNativeClass != 0) {
3587 nativeRecordButtons(true, false, true);
Leon Scroggins8cdad882009-06-30 08:47:04 -04003588 if (inEditingMode()) {
3589 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
3590 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003591 }
3592 } else {
3593 // If our window gained focus, but we do not have it, do not
Cary Clarkd6982c92009-05-29 11:02:22 -04003594 // draw the cursor ring.
3595 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003596 // We do not call nativeRecordButtons here because we assume
3597 // that when we lost focus, or window focus, it got called with
3598 // false for the first parameter
3599 }
3600 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07003601 if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003602 /*
3603 * The zoom controls come in their own window, so our window
Cary Clarkd6982c92009-05-29 11:02:22 -04003604 * loses focus. Our policy is to not draw the cursor ring if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003605 * our window is not focused, but this is an exception since
3606 * the user can still navigate the web page with the zoom
3607 * controls showing.
3608 */
Cary Clarkd6982c92009-05-29 11:02:22 -04003609 // If our window has lost focus, stop drawing the cursor ring
3610 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003611 }
3612 mGotKeyDown = false;
3613 mShiftIsPressed = false;
3614 if (mNativeClass != 0) {
3615 nativeRecordButtons(false, false, true);
3616 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003617 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003618 }
3619 invalidate();
3620 super.onWindowFocusChanged(hasWindowFocus);
3621 }
3622
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003623 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003624 * Pass a message to WebCore Thread, telling the WebCore::Page's
3625 * FocusController to be "inactive" so that it will
3626 * not draw the blinking cursor. It gets set to "active" to draw the cursor
3627 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003628 */
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003629 private void setFocusControllerInactive() {
Leon Scrogginsfd06bc82009-06-08 13:22:02 -04003630 // Do not need to also check whether mWebViewCore is null, because
3631 // mNativeClass is only set if mWebViewCore is non null
3632 if (mNativeClass == 0) return;
Leon Scroggins8cdad882009-06-30 08:47:04 -04003633 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003634 }
3635
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003636 @Override
3637 protected void onFocusChanged(boolean focused, int direction,
3638 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003639 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003640 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
3641 }
3642 if (focused) {
3643 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003644 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003645 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003646 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003647 if (mNativeClass != 0) {
3648 nativeRecordButtons(true, false, true);
3649 }
3650 //} else {
3651 // The WebView has gained focus while we do not have
3652 // windowfocus. When our window lost focus, we should have
3653 // called nativeRecordButtons(false...)
3654 }
3655 } else {
3656 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04003657 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003658 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003659 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003660 if (mNativeClass != 0) {
3661 nativeRecordButtons(false, false, true);
3662 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003663 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003664 }
3665 mGotKeyDown = false;
3666 }
3667
3668 super.onFocusChanged(focused, direction, previouslyFocusedRect);
3669 }
3670
3671 @Override
3672 protected void onSizeChanged(int w, int h, int ow, int oh) {
3673 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003674 // Center zooming to the center of the screen.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003675 mZoomCenterX = getViewWidth() * .5f;
3676 mZoomCenterY = getViewHeight() * .5f;
3677
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003678 // update mMinZoomScale if the minimum zoom scale is not fixed
3679 if (!mMinZoomScaleFixed) {
3680 mMinZoomScale = (float) getViewWidth()
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003681 / Math.max(ZOOM_OUT_WIDTH, mDrawHistory ? mHistoryPicture
3682 .getWidth() : (mZoomOverviewWidth > 0 ?
3683 mZoomOverviewWidth : mContentWidth));
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;
3816 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
3817 EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
3818 (eventTime - mLastTouchUpTime), eventTime);
3819 }
3820 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003821 // Trigger the link
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003822 if (mTouchMode == TOUCH_INIT_MODE
3823 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003824 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3825 .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
3826 }
3827 // Remember where the motion event started
3828 mLastTouchX = x;
3829 mLastTouchY = y;
3830 mLastTouchTime = eventTime;
3831 mVelocityTracker = VelocityTracker.obtain();
3832 mSnapScrollMode = SNAP_NONE;
3833 break;
3834 }
3835 case MotionEvent.ACTION_MOVE: {
Cary Clarkd6982c92009-05-29 11:02:22 -04003836 if (mTouchMode == TOUCH_DONE_MODE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003837 || mTouchMode == SCROLL_ZOOM_ANIMATION_IN
3838 || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
3839 // no dragging during scroll zoom animation
3840 break;
3841 }
3842 if (mTouchMode == SCROLL_ZOOM_OUT) {
3843 // while fully zoomed out, move the virtual window
3844 moveZoomScrollWindow(x, y);
3845 break;
3846 }
3847 mVelocityTracker.addMovement(ev);
3848
3849 if (mTouchMode != TOUCH_DRAG_MODE) {
3850 if (mTouchMode == TOUCH_SELECT_MODE) {
3851 mSelectX = mScrollX + (int) x;
3852 mSelectY = mScrollY + (int) y;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003853 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003854 Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
3855 }
3856 nativeMoveSelection(viewToContent(mSelectX)
3857 , viewToContent(mSelectY), true);
3858 invalidate();
3859 break;
3860 }
3861 if (mPreventDrag || (deltaX * deltaX + deltaY * deltaY)
3862 < mTouchSlopSquare) {
3863 break;
3864 }
3865
3866 if (mTouchMode == TOUCH_SHORTPRESS_MODE
3867 || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3868 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003869 } else if (mTouchMode == TOUCH_INIT_MODE
3870 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003871 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
3872 }
3873
3874 // if it starts nearly horizontal or vertical, enforce it
3875 int ax = Math.abs(deltaX);
3876 int ay = Math.abs(deltaY);
3877 if (ax > MAX_SLOPE_FOR_DIAG * ay) {
3878 mSnapScrollMode = SNAP_X;
3879 mSnapPositive = deltaX > 0;
3880 } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
3881 mSnapScrollMode = SNAP_Y;
3882 mSnapPositive = deltaY > 0;
3883 }
3884
3885 mTouchMode = TOUCH_DRAG_MODE;
3886 WebViewCore.pauseUpdate(mWebViewCore);
Leon Scroggins72543e12009-07-23 15:29:45 -04003887 if (!mDragFromTextInput) {
3888 nativeHideCursor();
3889 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003890 WebSettings settings = getSettings();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003891 if (settings.supportZoom() && !mInZoomOverview
The Android Open Source Project10592532009-03-18 17:39:46 -07003892 && settings.getBuiltInZoomControls()
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003893 && !mZoomButtonsController.isVisible()
Cary Clarkd6982c92009-05-29 11:02:22 -04003894 && (canZoomScrollOut() ||
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003895 mMinZoomScale < mMaxZoomScale)) {
3896 mZoomButtonsController.setVisible(true);
3897 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003898 }
3899
3900 // do pan
3901 int newScrollX = pinLocX(mScrollX + deltaX);
3902 deltaX = newScrollX - mScrollX;
3903 int newScrollY = pinLocY(mScrollY + deltaY);
3904 deltaY = newScrollY - mScrollY;
3905 boolean done = false;
3906 if (deltaX == 0 && deltaY == 0) {
3907 done = true;
3908 } else {
3909 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
3910 int ax = Math.abs(deltaX);
3911 int ay = Math.abs(deltaY);
3912 if (mSnapScrollMode == SNAP_X) {
3913 // radical change means getting out of snap mode
3914 if (ay > MAX_SLOPE_FOR_DIAG * ax
3915 && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
3916 mSnapScrollMode = SNAP_NONE;
3917 }
3918 // reverse direction means lock in the snap mode
3919 if ((ax > MAX_SLOPE_FOR_DIAG * ay) &&
Cary Clarkd6982c92009-05-29 11:02:22 -04003920 ((mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003921 deltaX < -mMinLockSnapReverseDistance)
3922 || (!mSnapPositive &&
3923 deltaX > mMinLockSnapReverseDistance))) {
3924 mSnapScrollMode = SNAP_X_LOCK;
3925 }
3926 } else {
3927 // radical change means getting out of snap mode
3928 if ((ax > MAX_SLOPE_FOR_DIAG * ay)
3929 && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
3930 mSnapScrollMode = SNAP_NONE;
3931 }
3932 // reverse direction means lock in the snap mode
3933 if ((ay > MAX_SLOPE_FOR_DIAG * ax) &&
Cary Clarkd6982c92009-05-29 11:02:22 -04003934 ((mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003935 deltaY < -mMinLockSnapReverseDistance)
Cary Clarkd6982c92009-05-29 11:02:22 -04003936 || (!mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003937 deltaY > mMinLockSnapReverseDistance))) {
3938 mSnapScrollMode = SNAP_Y_LOCK;
3939 }
3940 }
3941 }
3942
3943 if (mSnapScrollMode == SNAP_X
3944 || mSnapScrollMode == SNAP_X_LOCK) {
3945 scrollBy(deltaX, 0);
3946 mLastTouchX = x;
3947 } else if (mSnapScrollMode == SNAP_Y
3948 || mSnapScrollMode == SNAP_Y_LOCK) {
3949 scrollBy(0, deltaY);
3950 mLastTouchY = y;
3951 } else {
3952 scrollBy(deltaX, deltaY);
3953 mLastTouchX = x;
3954 mLastTouchY = y;
3955 }
3956 mLastTouchTime = eventTime;
3957 mUserScroll = true;
3958 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003959
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003960 if (!getSettings().getBuiltInZoomControls() && !mInZoomOverview) {
The Android Open Source Project10592532009-03-18 17:39:46 -07003961 boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
3962 boolean showMagnify = canZoomScrollOut();
3963 if (mZoomControls != null && (showPlusMinus || showMagnify)) {
3964 if (mZoomControls.getVisibility() == View.VISIBLE) {
3965 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
3966 } else {
3967 mZoomControls.show(showPlusMinus, showMagnify);
3968 }
3969 mPrivateHandler.postDelayed(mZoomControlRunnable,
3970 ZOOM_CONTROLS_TIMEOUT);
3971 }
3972 }
3973
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003974 if (done) {
3975 // return false to indicate that we can't pan out of the
3976 // view space
3977 return false;
3978 }
3979 break;
3980 }
3981 case MotionEvent.ACTION_UP: {
3982 mLastTouchUpTime = eventTime;
3983 switch (mTouchMode) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003984 case TOUCH_DOUBLE_TAP_MODE: // double tap
3985 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
3986 doDoubleTap();
3987 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003988 case TOUCH_INIT_MODE: // tap
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003989 if (ENABLE_DOUBLETAP_ZOOM) {
3990 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
3991 if (!mPreventDrag) {
3992 mPrivateHandler.sendMessageDelayed(
3993 mPrivateHandler.obtainMessage(
3994 RELEASE_SINGLE_TAP),
3995 ViewConfiguration.getDoubleTapTimeout());
3996 }
3997 break;
3998 }
3999 // fall through
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004000 case TOUCH_SHORTPRESS_START_MODE:
4001 case TOUCH_SHORTPRESS_MODE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004002 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004003 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
4004 mTouchMode = TOUCH_DONE_MODE;
4005 doShortPress();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004006 break;
4007 case TOUCH_SELECT_MODE:
4008 commitCopy();
4009 mTouchSelection = false;
4010 break;
4011 case SCROLL_ZOOM_ANIMATION_IN:
4012 case SCROLL_ZOOM_ANIMATION_OUT:
4013 // no action during scroll animation
4014 break;
4015 case SCROLL_ZOOM_OUT:
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004016 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004017 Log.v(LOGTAG, "ACTION_UP SCROLL_ZOOM_OUT"
4018 + " eventTime - mLastTouchTime="
4019 + (eventTime - mLastTouchTime));
4020 }
4021 // for now, always zoom back when the drag completes
4022 if (true || eventTime - mLastTouchTime < TAP_TIMEOUT) {
4023 // but if we tap, zoom in where we tap
4024 if (eventTime - mLastTouchTime < TAP_TIMEOUT) {
4025 zoomScrollTap(x, y);
4026 }
4027 // start zooming in back to the original view
4028 setZoomScrollIn();
4029 mTouchMode = SCROLL_ZOOM_ANIMATION_IN;
4030 invalidate();
4031 }
4032 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004033 case TOUCH_DRAG_MODE:
4034 // if the user waits a while w/o moving before the
4035 // up, we don't want to do a fling
4036 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
4037 mVelocityTracker.addMovement(ev);
4038 doFling();
4039 break;
4040 }
4041 WebViewCore.resumeUpdate(mWebViewCore);
4042 break;
4043 case TOUCH_DRAG_START_MODE:
4044 case TOUCH_DONE_MODE:
4045 // do nothing
4046 break;
4047 }
4048 // we also use mVelocityTracker == null to tell us that we are
4049 // not "moving around", so we can take the slower/prettier
4050 // mode in the drawing code
4051 if (mVelocityTracker != null) {
4052 mVelocityTracker.recycle();
4053 mVelocityTracker = null;
4054 }
4055 break;
4056 }
4057 case MotionEvent.ACTION_CANCEL: {
4058 // we also use mVelocityTracker == null to tell us that we are
4059 // not "moving around", so we can take the slower/prettier
4060 // mode in the drawing code
4061 if (mVelocityTracker != null) {
4062 mVelocityTracker.recycle();
4063 mVelocityTracker = null;
4064 }
4065 if (mTouchMode == SCROLL_ZOOM_OUT ||
4066 mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
4067 scrollTo(mZoomScrollX, mZoomScrollY);
4068 } else if (mTouchMode == TOUCH_DRAG_MODE) {
4069 WebViewCore.resumeUpdate(mWebViewCore);
4070 }
4071 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4072 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004073 mTouchMode = TOUCH_DONE_MODE;
Cary Clarke872f3a2009-06-11 09:51:11 -04004074 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004075 break;
4076 }
4077 }
4078 return true;
4079 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004081 private long mTrackballFirstTime = 0;
4082 private long mTrackballLastTime = 0;
4083 private float mTrackballRemainsX = 0.0f;
4084 private float mTrackballRemainsY = 0.0f;
4085 private int mTrackballXMove = 0;
4086 private int mTrackballYMove = 0;
4087 private boolean mExtendSelection = false;
4088 private boolean mTouchSelection = false;
4089 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
4090 private static final int TRACKBALL_TIMEOUT = 200;
4091 private static final int TRACKBALL_WAIT = 100;
4092 private static final int TRACKBALL_SCALE = 400;
4093 private static final int TRACKBALL_SCROLL_COUNT = 5;
4094 private static final int TRACKBALL_MOVE_COUNT = 10;
4095 private static final int TRACKBALL_MULTIPLIER = 3;
4096 private static final int SELECT_CURSOR_OFFSET = 16;
4097 private int mSelectX = 0;
4098 private int mSelectY = 0;
4099 private boolean mShiftIsPressed = false;
4100 private boolean mTrackballDown = false;
4101 private long mTrackballUpTime = 0;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004102 private long mLastCursorTime = 0;
4103 private Rect mLastCursorBounds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004104
4105 // Set by default; BrowserActivity clears to interpret trackball data
Cary Clarkd6982c92009-05-29 11:02:22 -04004106 // directly for movement. Currently, the framework only passes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004107 // arrow key events, not trackball events, from one child to the next
4108 private boolean mMapTrackballToArrowKeys = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004110 public void setMapTrackballToArrowKeys(boolean setMap) {
4111 mMapTrackballToArrowKeys = setMap;
4112 }
4113
4114 void resetTrackballTime() {
4115 mTrackballLastTime = 0;
4116 }
4117
4118 @Override
4119 public boolean onTrackballEvent(MotionEvent ev) {
4120 long time = ev.getEventTime();
4121 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
4122 if (ev.getY() > 0) pageDown(true);
4123 if (ev.getY() < 0) pageUp(true);
4124 return true;
4125 }
4126 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004127 mPrivateHandler.removeMessages(SWITCH_TO_CLICK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004128 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004129 if (mNativeClass == 0) {
4130 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004131 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004132 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004133 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
4134 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
4135 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004136 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004137 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004138 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004139 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004140 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004141 }
4142 if (isInTouchMode()) requestFocusFromTouch();
4143 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04004144 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004145 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04004146 // LONG_PRESS_CENTER is set in common onKeyDown
4147 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004148 mTrackballDown = false;
4149 mTrackballUpTime = time;
4150 if (mShiftIsPressed) {
4151 if (mExtendSelection) {
4152 commitCopy();
4153 } else {
4154 mExtendSelection = true;
4155 }
4156 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004157 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004158 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004159 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004160 );
4161 }
4162 return false; // let common code in onKeyUp at it
4163 }
4164 if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004165 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004166 return false;
4167 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004168 // no move if we're still waiting on SWITCH_TO_CLICK timeout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004169 if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004170 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004171 return true;
4172 }
4173 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004174 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004175 return true; // discard move if trackball is down
4176 }
4177 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004178 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004179 return true;
4180 }
4181 // TODO: alternatively we can do panning as touch does
4182 switchOutDrawHistory();
4183 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004184 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004185 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004186 + time + " last=" + mTrackballLastTime);
4187 }
4188 mTrackballFirstTime = time;
4189 mTrackballXMove = mTrackballYMove = 0;
4190 }
4191 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004192 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004193 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
4194 }
4195 mTrackballRemainsX += ev.getX();
4196 mTrackballRemainsY += ev.getY();
4197 doTrackball(time);
4198 return true;
4199 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004201 void moveSelection(float xRate, float yRate) {
4202 if (mNativeClass == 0)
4203 return;
4204 int width = getViewWidth();
4205 int height = getViewHeight();
4206 mSelectX += scaleTrackballX(xRate, width);
4207 mSelectY += scaleTrackballY(yRate, height);
4208 int maxX = width + mScrollX;
4209 int maxY = height + mScrollY;
4210 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
4211 , mSelectX));
4212 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
4213 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004214 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004215 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004216 + " mSelectX=" + mSelectX
4217 + " mSelectY=" + mSelectY
4218 + " mScrollX=" + mScrollX
4219 + " mScrollY=" + mScrollY
4220 + " xRate=" + xRate
4221 + " yRate=" + yRate
4222 );
4223 }
4224 nativeMoveSelection(viewToContent(mSelectX)
4225 , viewToContent(mSelectY), mExtendSelection);
4226 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004227 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004228 : 0;
4229 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004230 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004231 : 0;
4232 pinScrollBy(scrollX, scrollY, true, 0);
4233 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
4234 requestRectangleOnScreen(select);
4235 invalidate();
4236 }
4237
4238 private int scaleTrackballX(float xRate, int width) {
4239 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
4240 int nextXMove = xMove;
4241 if (xMove > 0) {
4242 if (xMove > mTrackballXMove) {
4243 xMove -= mTrackballXMove;
4244 }
4245 } else if (xMove < mTrackballXMove) {
4246 xMove -= mTrackballXMove;
4247 }
4248 mTrackballXMove = nextXMove;
4249 return xMove;
4250 }
4251
4252 private int scaleTrackballY(float yRate, int height) {
4253 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
4254 int nextYMove = yMove;
4255 if (yMove > 0) {
4256 if (yMove > mTrackballYMove) {
4257 yMove -= mTrackballYMove;
4258 }
4259 } else if (yMove < mTrackballYMove) {
4260 yMove -= mTrackballYMove;
4261 }
4262 mTrackballYMove = nextYMove;
4263 return yMove;
4264 }
4265
4266 private int keyCodeToSoundsEffect(int keyCode) {
4267 switch(keyCode) {
4268 case KeyEvent.KEYCODE_DPAD_UP:
4269 return SoundEffectConstants.NAVIGATION_UP;
4270 case KeyEvent.KEYCODE_DPAD_RIGHT:
4271 return SoundEffectConstants.NAVIGATION_RIGHT;
4272 case KeyEvent.KEYCODE_DPAD_DOWN:
4273 return SoundEffectConstants.NAVIGATION_DOWN;
4274 case KeyEvent.KEYCODE_DPAD_LEFT:
4275 return SoundEffectConstants.NAVIGATION_LEFT;
4276 }
4277 throw new IllegalArgumentException("keyCode must be one of " +
4278 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
4279 "KEYCODE_DPAD_LEFT}.");
4280 }
4281
4282 private void doTrackball(long time) {
4283 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
4284 if (elapsed == 0) {
4285 elapsed = TRACKBALL_TIMEOUT;
4286 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004287 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004288 float yRate = mTrackballRemainsY * 1000 / elapsed;
4289 if (mShiftIsPressed) {
4290 moveSelection(xRate, yRate);
4291 mTrackballRemainsX = mTrackballRemainsY = 0;
4292 return;
4293 }
4294 float ax = Math.abs(xRate);
4295 float ay = Math.abs(yRate);
4296 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004297 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004298 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
4299 + " xRate=" + xRate
4300 + " yRate=" + yRate
4301 + " mTrackballRemainsX=" + mTrackballRemainsX
4302 + " mTrackballRemainsY=" + mTrackballRemainsY);
4303 }
4304 int width = mContentWidth - getViewWidth();
4305 int height = mContentHeight - getViewHeight();
4306 if (width < 0) width = 0;
4307 if (height < 0) height = 0;
4308 if (mTouchMode == SCROLL_ZOOM_OUT) {
4309 int oldX = mZoomScrollX;
4310 int oldY = mZoomScrollY;
4311 int maxWH = Math.max(width, height);
4312 mZoomScrollX += scaleTrackballX(xRate, maxWH);
4313 mZoomScrollY += scaleTrackballY(yRate, maxWH);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004314 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004315 Log.v(LOGTAG, "doTrackball SCROLL_ZOOM_OUT"
Cary Clarkd6982c92009-05-29 11:02:22 -04004316 + " mZoomScrollX=" + mZoomScrollX
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004317 + " mZoomScrollY=" + mZoomScrollY);
4318 }
4319 mZoomScrollX = Math.min(width, Math.max(0, mZoomScrollX));
4320 mZoomScrollY = Math.min(height, Math.max(0, mZoomScrollY));
4321 if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
4322 invalidate();
4323 }
4324 mTrackballRemainsX = mTrackballRemainsY = 0;
4325 return;
4326 }
4327 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
4328 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
4329 maxA = Math.max(ax, ay);
4330 int count = Math.max(0, (int) maxA);
4331 int oldScrollX = mScrollX;
4332 int oldScrollY = mScrollY;
4333 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004334 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
4335 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004336 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
4337 KeyEvent.KEYCODE_DPAD_RIGHT;
4338 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004339 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004340 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004341 + " count=" + count
4342 + " mTrackballRemainsX=" + mTrackballRemainsX
4343 + " mTrackballRemainsY=" + mTrackballRemainsY);
4344 }
Cary Clark215b72c2009-06-26 14:38:43 -04004345 if (navHandledKey(selectKeyCode, count, false, time, false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004346 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
4347 }
4348 mTrackballRemainsX = mTrackballRemainsY = 0;
4349 }
4350 if (count >= TRACKBALL_SCROLL_COUNT) {
4351 int xMove = scaleTrackballX(xRate, width);
4352 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004353 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004354 Log.v(LOGTAG, "doTrackball pinScrollBy"
4355 + " count=" + count
4356 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04004357 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
4358 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004359 );
4360 }
4361 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
4362 xMove = 0;
4363 }
4364 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
4365 yMove = 0;
4366 }
4367 if (xMove != 0 || yMove != 0) {
4368 pinScrollBy(xMove, yMove, true, 0);
4369 }
4370 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004371 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004372 }
4373
4374 public void flingScroll(int vx, int vy) {
4375 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
4376 int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
Cary Clarkd6982c92009-05-29 11:02:22 -04004377
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004378 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
4379 invalidate();
4380 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004381
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004382 private void doFling() {
4383 if (mVelocityTracker == null) {
4384 return;
4385 }
4386 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
4387 int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
4388
Romain Guy4296fc42009-07-06 11:48:52 -07004389 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004390 int vx = (int) mVelocityTracker.getXVelocity();
4391 int vy = (int) mVelocityTracker.getYVelocity();
4392
4393 if (mSnapScrollMode != SNAP_NONE) {
4394 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_X_LOCK) {
4395 vy = 0;
4396 } else {
4397 vx = 0;
4398 }
4399 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004400
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004401 if (true /* EMG release: make our fling more like Maps' */) {
4402 // maps cuts their velocity in half
4403 vx = vx * 3 / 4;
4404 vy = vy * 3 / 4;
4405 }
4406
4407 mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
4408 // TODO: duration is calculated based on velocity, if the range is
4409 // small, the animation will stop before duration is up. We may
4410 // want to calculate how long the animation is going to run to precisely
4411 // resume the webcore update.
4412 final int time = mScroller.getDuration();
4413 mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_UPDATE, time);
4414 invalidate();
4415 }
4416
4417 private boolean zoomWithPreview(float scale) {
4418 float oldScale = mActualScale;
Grace Kloba675c7d22009-07-23 09:21:21 -07004419 mInitialScrollX = mScrollX;
4420 mInitialScrollY = mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004421
Grace Kloba25737912009-06-19 12:42:47 -07004422 // snap to DEFAULT_SCALE if it is close
Grace Kloba0d8b77c2009-06-25 11:20:51 -07004423 if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
4424 scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004425 }
4426
4427 setNewZoomScale(scale, false);
4428
4429 if (oldScale != mActualScale) {
4430 // use mZoomPickerScale to see zoom preview first
4431 mZoomStart = SystemClock.uptimeMillis();
4432 mInvInitialZoomScale = 1.0f / oldScale;
4433 mInvFinalZoomScale = 1.0f / mActualScale;
4434 mZoomScale = mActualScale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004435 if (!mInZoomOverview) {
4436 mLastScale = scale;
4437 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004438 invalidate();
4439 return true;
4440 } else {
4441 return false;
4442 }
4443 }
4444
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004445 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004446 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
4447 * in charge of installing this view to the view hierarchy. This view will
4448 * become visible when the user starts scrolling via touch and fade away if
4449 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004450 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07004451 * API version 3 introduces a built-in zoom mechanism that is shown
4452 * automatically by the MapView. This is the preferred approach for
4453 * showing the zoom UI.
4454 *
4455 * @deprecated The built-in zoom mechanism is preferred, see
4456 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004457 */
The Android Open Source Project10592532009-03-18 17:39:46 -07004458 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004459 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07004460 if (!getSettings().supportZoom()) {
4461 Log.w(LOGTAG, "This WebView doesn't support zoom.");
4462 return null;
4463 }
4464 if (mZoomControls == null) {
4465 mZoomControls = createZoomControls();
Cary Clarkd6982c92009-05-29 11:02:22 -04004466
The Android Open Source Project10592532009-03-18 17:39:46 -07004467 /*
4468 * need to be set to VISIBLE first so that getMeasuredHeight() in
4469 * {@link #onSizeChanged()} can return the measured value for proper
4470 * layout.
4471 */
4472 mZoomControls.setVisibility(View.VISIBLE);
4473 mZoomControlRunnable = new Runnable() {
4474 public void run() {
Cary Clarkd6982c92009-05-29 11:02:22 -04004475
The Android Open Source Project10592532009-03-18 17:39:46 -07004476 /* Don't dismiss the controls if the user has
4477 * focus on them. Wait and check again later.
4478 */
4479 if (!mZoomControls.hasFocus()) {
4480 mZoomControls.hide();
4481 } else {
4482 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4483 mPrivateHandler.postDelayed(mZoomControlRunnable,
4484 ZOOM_CONTROLS_TIMEOUT);
4485 }
4486 }
4487 };
4488 }
4489 return mZoomControls;
4490 }
4491
4492 private ExtendedZoomControls createZoomControls() {
4493 ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
4494 , null);
4495 zoomControls.setOnZoomInClickListener(new OnClickListener() {
4496 public void onClick(View v) {
4497 // reset time out
4498 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4499 mPrivateHandler.postDelayed(mZoomControlRunnable,
4500 ZOOM_CONTROLS_TIMEOUT);
4501 zoomIn();
4502 }
4503 });
4504 zoomControls.setOnZoomOutClickListener(new OnClickListener() {
4505 public void onClick(View v) {
4506 // reset time out
4507 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4508 mPrivateHandler.postDelayed(mZoomControlRunnable,
4509 ZOOM_CONTROLS_TIMEOUT);
4510 zoomOut();
4511 }
4512 });
4513 zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() {
4514 public void onClick(View v) {
4515 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4516 mPrivateHandler.postDelayed(mZoomControlRunnable,
4517 ZOOM_CONTROLS_TIMEOUT);
4518 zoomScrollOut();
4519 }
4520 });
4521 return zoomControls;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004522 }
4523
4524 /**
4525 * Gets the {@link ZoomButtonsController} which can be used to add
4526 * additional buttons to the zoom controls window.
Cary Clarkd6982c92009-05-29 11:02:22 -04004527 *
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004528 * @return The instance of {@link ZoomButtonsController} used by this class,
4529 * or null if it is unavailable.
The Android Open Source Project10592532009-03-18 17:39:46 -07004530 * @hide
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004531 */
4532 public ZoomButtonsController getZoomButtonsController() {
4533 return mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004534 }
4535
4536 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004537 * Perform zoom in in the webview
4538 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
4539 */
4540 public boolean zoomIn() {
4541 // TODO: alternatively we can disallow this during draw history mode
4542 switchOutDrawHistory();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004543 // Center zooming to the center of the screen.
4544 mZoomCenterX = getViewWidth() * .5f;
4545 mZoomCenterY = getViewHeight() * .5f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004546 return zoomWithPreview(mActualScale * 1.25f);
4547 }
4548
4549 /**
4550 * Perform zoom out in the webview
4551 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
4552 */
4553 public boolean zoomOut() {
4554 // TODO: alternatively we can disallow this during draw history mode
4555 switchOutDrawHistory();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004556 // Center zooming to the center of the screen.
4557 mZoomCenterX = getViewWidth() * .5f;
4558 mZoomCenterY = getViewHeight() * .5f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004559 return zoomWithPreview(mActualScale * 0.8f);
4560 }
4561
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004562 private void updateSelection() {
4563 if (mNativeClass == 0) {
4564 return;
4565 }
4566 // mLastTouchX and mLastTouchY are the point in the current viewport
4567 int contentX = viewToContent((int) mLastTouchX + mScrollX);
4568 int contentY = viewToContent((int) mLastTouchY + mScrollY);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004569 Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
4570 contentX + mNavSlop, contentY + mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004571 nativeSelectBestAt(rect);
4572 }
4573
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004574 /**
Leon Scroggins72543e12009-07-23 15:29:45 -04004575 * Scroll the focused text field/area to match the WebTextView
4576 * @param x New x position of the WebTextView in view coordinates
4577 * @param y New y position of the WebTextView in view coordinates
4578 */
4579 /*package*/ void scrollFocusedTextInput(int x, int y) {
4580 if (!inEditingMode() || mWebViewCore == null) {
4581 return;
4582 }
4583 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, viewToContent(x),
4584 viewToContent(y));
4585 }
4586
4587 /**
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004588 * Set our starting point and time for a drag from the WebTextView.
4589 */
4590 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
4591 if (!inEditingMode()) {
4592 return;
4593 }
4594 mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
4595 mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
4596 mLastTouchTime = eventTime;
4597 if (!mScroller.isFinished()) {
4598 mScroller.abortAnimation();
4599 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
4600 }
4601 mSnapScrollMode = SNAP_NONE;
4602 mVelocityTracker = VelocityTracker.obtain();
4603 mTouchMode = TOUCH_DRAG_START_MODE;
4604 }
4605
4606 /**
4607 * Given a motion event from the WebTextView, set its location to our
4608 * coordinates, and handle the event.
4609 */
4610 /*package*/ boolean textFieldDrag(MotionEvent event) {
4611 if (!inEditingMode()) {
4612 return false;
4613 }
Leon Scroggins72543e12009-07-23 15:29:45 -04004614 mDragFromTextInput = true;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004615 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
4616 (float) (mWebTextView.getTop() - mScrollY));
Leon Scroggins72543e12009-07-23 15:29:45 -04004617 boolean result = onTouchEvent(event);
4618 mDragFromTextInput = false;
4619 return result;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004620 }
4621
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004622 /*package*/ void shortPressOnTextField() {
4623 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004624 View v = mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004625 int x = viewToContent((v.getLeft() + v.getRight()) >> 1);
4626 int y = viewToContent((v.getTop() + v.getBottom()) >> 1);
Cary Clark8f9ff7e2009-05-14 10:09:26 -04004627 nativeMotionUp(x, y, mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004628 }
4629 }
4630
4631 private void doShortPress() {
4632 if (mNativeClass == 0) {
4633 return;
4634 }
4635 switchOutDrawHistory();
4636 // mLastTouchX and mLastTouchY are the point in the current viewport
4637 int contentX = viewToContent((int) mLastTouchX + mScrollX);
4638 int contentY = viewToContent((int) mLastTouchY + mScrollY);
Cary Clark8f9ff7e2009-05-14 10:09:26 -04004639 if (nativeMotionUp(contentX, contentY, mNavSlop)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004640 if (mLogEvent) {
4641 Checkin.updateStats(mContext.getContentResolver(),
4642 Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
4643 }
4644 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004645 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004646 playSoundEffect(SoundEffectConstants.CLICK);
4647 }
4648 }
4649
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004650 private void doDoubleTap() {
4651 if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
4652 return;
4653 }
4654 mZoomCenterX = mLastTouchX;
4655 mZoomCenterY = mLastTouchY;
4656 mInZoomOverview = !mInZoomOverview;
4657 if (mInZoomOverview) {
4658 float newScale = (float) getViewWidth()
4659 / (mZoomOverviewWidth > 0 ? mZoomOverviewWidth
4660 : mContentWidth);
4661 if (Math.abs(newScale - mActualScale) < 0.01) {
4662 mInZoomOverview = !mInZoomOverview;
4663 // as it is already full screen, do nothing.
4664 return;
4665 }
4666 if (getSettings().getBuiltInZoomControls()) {
4667 if (mZoomButtonsController.isVisible()) {
4668 mZoomButtonsController.setVisible(false);
4669 }
4670 } else {
4671 if (mZoomControlRunnable != null) {
4672 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4673 }
4674 if (mZoomControls != null) {
4675 mZoomControls.hide();
4676 }
4677 }
4678 zoomWithPreview(newScale);
4679 } else {
4680 // mLastTouchX and mLastTouchY are the point in the current viewport
4681 int contentX = viewToContent((int) mLastTouchX + mScrollX);
4682 int contentY = viewToContent((int) mLastTouchY + mScrollY);
4683 int left = nativeGetBlockLeftEdge(contentX, contentY);
4684 if (left != NO_LEFTEDGE) {
4685 // add a 5pt padding to the left edge. Re-calculate the zoom
4686 // center so that the new scroll x will be on the left edge.
4687 mZoomCenterX = left < 5 ? 0 : (left - 5) * mLastScale
4688 * mActualScale / (mLastScale - mActualScale);
4689 }
4690 zoomWithPreview(mLastScale);
4691 }
4692 }
4693
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004694 // Called by JNI to handle a touch on a node representing an email address,
4695 // address, or phone number
4696 private void overrideLoading(String url) {
4697 mCallbackProxy.uiOverrideUrlLoading(url);
4698 }
4699
Derek Sollenberger945f3b42009-07-02 10:05:21 -04004700 // called by JNI
4701 private void sendPluginState(int state) {
4702 WebViewCore.PluginStateData psd = new WebViewCore.PluginStateData();
4703 psd.mFrame = nativeCursorFramePointer();
4704 psd.mNode = nativeCursorNodePointer();
4705 psd.mState = state;
4706 mWebViewCore.sendMessage(EventHub.PLUGIN_STATE, psd);
4707 }
4708
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004709 @Override
4710 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
4711 boolean result = false;
4712 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004713 result = mWebTextView.requestFocus(direction,
4714 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004715 } else {
4716 result = super.requestFocus(direction, previouslyFocusedRect);
4717 if (mWebViewCore.getSettings().getNeedInitialFocus()) {
4718 // For cases such as GMail, where we gain focus from a direction,
4719 // we want to move to the first available link.
4720 // FIXME: If there are no visible links, we may not want to
4721 int fakeKeyDirection = 0;
4722 switch(direction) {
4723 case View.FOCUS_UP:
4724 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
4725 break;
4726 case View.FOCUS_DOWN:
4727 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
4728 break;
4729 case View.FOCUS_LEFT:
4730 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
4731 break;
4732 case View.FOCUS_RIGHT:
4733 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
4734 break;
4735 default:
4736 return result;
4737 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004738 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Cary Clark215b72c2009-06-26 14:38:43 -04004739 navHandledKey(fakeKeyDirection, 1, true, 0, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004740 }
4741 }
4742 }
4743 return result;
4744 }
4745
4746 @Override
4747 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
4748 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4749
4750 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
4751 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
4752 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
4753 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
4754
4755 int measuredHeight = heightSize;
4756 int measuredWidth = widthSize;
4757
4758 // Grab the content size from WebViewCore.
4759 int contentHeight = mContentHeight;
4760 int contentWidth = mContentWidth;
4761
4762// Log.d(LOGTAG, "------- measure " + heightMode);
4763
4764 if (heightMode != MeasureSpec.EXACTLY) {
4765 mHeightCanMeasure = true;
4766 measuredHeight = contentHeight;
4767 if (heightMode == MeasureSpec.AT_MOST) {
4768 // If we are larger than the AT_MOST height, then our height can
4769 // no longer be measured and we should scroll internally.
4770 if (measuredHeight > heightSize) {
4771 measuredHeight = heightSize;
4772 mHeightCanMeasure = false;
4773 }
4774 }
4775 } else {
4776 mHeightCanMeasure = false;
4777 }
4778 if (mNativeClass != 0) {
4779 nativeSetHeightCanMeasure(mHeightCanMeasure);
4780 }
4781 // For the width, always use the given size unless unspecified.
4782 if (widthMode == MeasureSpec.UNSPECIFIED) {
4783 mWidthCanMeasure = true;
4784 measuredWidth = contentWidth;
4785 } else {
4786 mWidthCanMeasure = false;
4787 }
4788
4789 synchronized (this) {
4790 setMeasuredDimension(measuredWidth, measuredHeight);
4791 }
4792 }
4793
4794 @Override
4795 public boolean requestChildRectangleOnScreen(View child,
4796 Rect rect,
4797 boolean immediate) {
4798 rect.offset(child.getLeft() - child.getScrollX(),
4799 child.getTop() - child.getScrollY());
4800
4801 int height = getHeight() - getHorizontalScrollbarHeight();
4802 int screenTop = mScrollY;
4803 int screenBottom = screenTop + height;
4804
4805 int scrollYDelta = 0;
4806
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004807 if (rect.bottom > screenBottom) {
4808 int oneThirdOfScreenHeight = height / 3;
4809 if (rect.height() > 2 * oneThirdOfScreenHeight) {
4810 // If the rectangle is too tall to fit in the bottom two thirds
4811 // of the screen, place it at the top.
4812 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004813 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004814 // If the rectangle will still fit on screen, we want its
4815 // top to be in the top third of the screen.
4816 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004817 }
4818 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004819 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004820 }
4821
4822 int width = getWidth() - getVerticalScrollbarWidth();
4823 int screenLeft = mScrollX;
4824 int screenRight = screenLeft + width;
4825
4826 int scrollXDelta = 0;
4827
4828 if (rect.right > screenRight && rect.left > screenLeft) {
4829 if (rect.width() > width) {
4830 scrollXDelta += (rect.left - screenLeft);
4831 } else {
4832 scrollXDelta += (rect.right - screenRight);
4833 }
4834 } else if (rect.left < screenLeft) {
4835 scrollXDelta -= (screenLeft - rect.left);
4836 }
4837
4838 if ((scrollYDelta | scrollXDelta) != 0) {
4839 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
4840 }
4841
4842 return false;
4843 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004844
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004845 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
4846 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04004847 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
4848 arg.mReplace = replace;
4849 arg.mNewStart = newStart;
4850 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004851 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04004852 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004853 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
4854 }
4855
4856 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkf958fcc2009-06-12 17:33:37 -04004857 if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) {
4858 mWebViewCore.sendMessage(EventHub.CLICK);
4859 }
Cary Clarkded054c2009-06-15 10:26:08 -04004860 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
4861 arg.mEvent = event;
4862 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004863 // Increase our text generation number, and pass it to webcore thread
4864 mTextGeneration++;
4865 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
4866 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04004867 // To make sure the host application, like Browser, has the up to date
4868 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004869 // document state.
4870 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
4871 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04004872 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004873 }
4874
4875 /* package */ WebViewCore getWebViewCore() {
4876 return mWebViewCore;
4877 }
4878
4879 //-------------------------------------------------------------------------
4880 // Methods can be called from a separate thread, like WebViewCore
4881 // If it needs to call the View system, it has to send message.
4882 //-------------------------------------------------------------------------
4883
4884 /**
4885 * General handler to receive message coming from webkit thread
4886 */
4887 class PrivateHandler extends Handler {
4888 @Override
4889 public void handleMessage(Message msg) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004890 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004891 Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
4892 > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004893 : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
4894 }
4895 switch (msg.what) {
4896 case REMEMBER_PASSWORD: {
4897 mDatabase.setUsernamePassword(
4898 msg.getData().getString("host"),
4899 msg.getData().getString("username"),
4900 msg.getData().getString("password"));
4901 ((Message) msg.obj).sendToTarget();
4902 break;
4903 }
4904 case NEVER_REMEMBER_PASSWORD: {
4905 mDatabase.setUsernamePassword(
4906 msg.getData().getString("host"), null, null);
4907 ((Message) msg.obj).sendToTarget();
4908 break;
4909 }
4910 case SWITCH_TO_SHORTPRESS: {
4911 if (mTouchMode == TOUCH_INIT_MODE) {
4912 mTouchMode = TOUCH_SHORTPRESS_START_MODE;
4913 updateSelection();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004914 } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
4915 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004916 }
4917 break;
4918 }
4919 case SWITCH_TO_LONGPRESS: {
Grace Kloba6c451b72009-06-25 12:25:30 -07004920 if (!mPreventDrag) {
4921 mTouchMode = TOUCH_DONE_MODE;
4922 performLongClick();
Grace Kloba959046c2009-06-25 14:28:04 -07004923 rebuildWebTextView();
Grace Kloba6c451b72009-06-25 12:25:30 -07004924 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004925 break;
4926 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004927 case RELEASE_SINGLE_TAP: {
4928 if (!mPreventDrag) {
4929 mTouchMode = TOUCH_DONE_MODE;
4930 doShortPress();
4931 }
4932 break;
4933 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004934 case SWITCH_TO_CLICK:
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004935 // The user clicked with the trackball, and did not click a
4936 // second time, so perform the action of a trackball single
4937 // click
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004938 mTouchMode = TOUCH_DONE_MODE;
Cary Clarkd6982c92009-05-29 11:02:22 -04004939 Rect visibleRect = sendOurVisibleRect();
4940 // Note that sendOurVisibleRect calls viewToContent, so the
4941 // coordinates should be in content coordinates.
4942 if (!nativeCursorIntersects(visibleRect)) {
4943 break;
4944 }
4945 nativeSetFollowedLink(true);
Cary Clark215b72c2009-06-26 14:38:43 -04004946 nativeUpdatePluginReceivesEvents();
Leon Scrogginsef6da8f2009-06-26 14:00:40 -04004947 WebViewCore.CursorData data = cursorData();
4948 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
Cary Clarkd6982c92009-05-29 11:02:22 -04004949 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004950 boolean isTextInput = nativeCursorIsTextInput();
4951 if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading(
4952 nativeCursorText())) {
Leon Scrogginsef6da8f2009-06-26 14:00:40 -04004953 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
4954 nativeCursorNodePointer());
Cary Clarkd6982c92009-05-29 11:02:22 -04004955 }
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004956 if (isTextInput) {
4957 rebuildWebTextView();
4958 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004959 break;
4960 case SCROLL_BY_MSG_ID:
4961 setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
4962 break;
4963 case SYNC_SCROLL_TO_MSG_ID:
4964 if (mUserScroll) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004965 // if user has scrolled explicitly, don't sync the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004966 // scroll position any more
4967 mUserScroll = false;
4968 break;
4969 }
4970 // fall through
4971 case SCROLL_TO_MSG_ID:
4972 if (setContentScrollTo(msg.arg1, msg.arg2)) {
4973 // if we can't scroll to the exact position due to pin,
Cary Clarkd6982c92009-05-29 11:02:22 -04004974 // send a message to WebCore to re-scroll when we get a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004975 // new picture
4976 mUserScroll = false;
4977 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
4978 msg.arg1, msg.arg2);
4979 }
4980 break;
4981 case SPAWN_SCROLL_TO_MSG_ID:
4982 spawnContentScrollTo(msg.arg1, msg.arg2);
4983 break;
4984 case NEW_PICTURE_MSG_ID:
4985 // called for new content
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004986 final int viewWidth = getViewWidth();
Cary Clarkd6982c92009-05-29 11:02:22 -04004987 final WebViewCore.DrawData draw =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004988 (WebViewCore.DrawData) msg.obj;
4989 final Point viewSize = draw.mViewPoint;
Grace Klobaef347ef2009-07-30 11:20:32 -07004990 boolean useWideViewport =
4991 mWebViewCore.getSettings().getUseWideViewPort();
4992 WebViewCore.RestoreState restoreState = draw.mRestoreState;
4993 if (restoreState != null) {
4994 mInZoomOverview = false;
4995 mLastScale = restoreState.mTextWrapScale;
4996 if (restoreState.mMinScale == 0) {
4997 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
4998 mMinZoomScaleFixed = false;
4999 } else {
5000 mMinZoomScale = restoreState.mMinScale;
5001 mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005002 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005003 if (restoreState.mMaxScale == 0) {
5004 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
5005 } else {
5006 mMaxZoomScale = restoreState.mMaxScale;
5007 }
5008 if (useWideViewport && restoreState.mViewScale == 0) {
5009 mInZoomOverview = ENABLE_DOUBLETAP_ZOOM;
5010 }
5011 setNewZoomScale(mLastScale, false);
5012 setContentScrollTo(restoreState.mScrollX,
5013 restoreState.mScrollY);
5014 // As we are on a new page, remove the WebTextView. This
5015 // is necessary for page loads driven by webkit, and in
5016 // particular when the user was on a password field, so
5017 // the WebTextView was visible.
5018 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005019 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005020 // We update the layout (i.e. request a layout from the
5021 // view system) if the last view size that we sent to
5022 // WebCore matches the view size of the picture we just
5023 // received in the fixed dimension.
5024 final boolean updateLayout = viewSize.x == mLastWidthSent
5025 && viewSize.y == mLastHeightSent;
Cary Clarkd6982c92009-05-29 11:02:22 -04005026 recordNewContentSize(draw.mWidthHeight.x,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005027 draw.mWidthHeight.y, updateLayout);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005028 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005029 Rect b = draw.mInvalRegion.getBounds();
5030 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
5031 b.left+","+b.top+","+b.right+","+b.bottom+"}");
5032 }
5033 invalidate(contentToView(draw.mInvalRegion.getBounds()));
5034 if (mPictureListener != null) {
5035 mPictureListener.onNewPicture(WebView.this, capturePicture());
5036 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005037 if (useWideViewport) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005038 mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
5039 draw.mViewPoint.x);
5040 }
5041 if (!mMinZoomScaleFixed) {
5042 mMinZoomScale = (float) viewWidth
5043 / Math.max(ZOOM_OUT_WIDTH,
5044 mZoomOverviewWidth > 0 ? mZoomOverviewWidth
5045 : mContentWidth);
5046 }
5047 if (!mDrawHistory && mInZoomOverview) {
5048 // fit the content width to the current view. Ignore
5049 // the rounding error case.
5050 if (Math.abs((viewWidth * mInvActualScale)
5051 - mZoomOverviewWidth) > 1) {
5052 zoomWithPreview((float) viewWidth
5053 / mZoomOverviewWidth);
5054 }
5055 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005056 break;
5057 case WEBCORE_INITIALIZED_MSG_ID:
5058 // nativeCreate sets mNativeClass to a non-zero value
5059 nativeCreate(msg.arg1);
5060 break;
5061 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
5062 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04005063 // and representing the same node as the pointer.
5064 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005065 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005066 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005067 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005068 int start = Selection.getSelectionStart(text);
5069 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005070 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005071 // Restore the selection, which may have been
5072 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005073 Spannable pword =
5074 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005075 Selection.setSelection(pword, start, end);
5076 // If the text entry has created more events, ignore
5077 // this one.
5078 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005079 mWebTextView.setTextAndKeepSelection(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005080 (String) msg.obj);
5081 }
5082 }
5083 break;
Cary Clark215b72c2009-06-26 14:38:43 -04005084 case MOVE_OUT_OF_PLUGIN:
5085 if (nativePluginEatsNavKey()) {
5086 navHandledKey(msg.arg1, 1, false, 0, true);
5087 }
5088 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005089 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04005090 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005091 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04005092 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005093 mWebTextView.bringIntoView();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005094 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005095 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005096 break;
Cary Clark243ea062009-06-25 10:49:32 -04005097 case CLEAR_TEXT_ENTRY:
5098 clearTextEntry();
5099 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005100 case INVAL_RECT_MSG_ID: {
5101 Rect r = (Rect)msg.obj;
5102 if (r == null) {
5103 invalidate();
5104 } else {
5105 // we need to scale r from content into view coords,
5106 // which viewInvalidate() does for us
5107 viewInvalidate(r.left, r.top, r.right, r.bottom);
5108 }
5109 break;
5110 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005111 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04005112 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005113 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005114 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005115 }
5116 break;
5117 case UPDATE_CLIPBOARD:
5118 String str = (String) msg.obj;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005119 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005120 Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
5121 }
5122 try {
5123 IClipboard clip = IClipboard.Stub.asInterface(
5124 ServiceManager.getService("clipboard"));
5125 clip.setClipboardText(str);
5126 } catch (android.os.RemoteException e) {
5127 Log.e(LOGTAG, "Clipboard failed", e);
5128 }
5129 break;
5130 case RESUME_WEBCORE_UPDATE:
5131 WebViewCore.resumeUpdate(mWebViewCore);
5132 break;
5133
Leon Scrogginse3225672009-06-03 15:53:13 -04005134 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005135 // as this is shared by keydown and trackballdown, reset all
5136 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04005137 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005138 mTrackballDown = false;
Leon Scrogginse3225672009-06-03 15:53:13 -04005139 // LONG_PRESS_CENTER is sent as a delayed message. If we
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005140 // switch to windows overview, the WebView will be
5141 // temporarily removed from the view system. In that case,
5142 // do nothing.
5143 if (getParent() != null) {
5144 performLongClick();
5145 }
5146 break;
5147
5148 case WEBCORE_NEED_TOUCH_EVENTS:
5149 mForwardTouchEvents = (msg.arg1 != 0);
5150 break;
5151
5152 case PREVENT_TOUCH_ID:
5153 if (msg.arg1 == MotionEvent.ACTION_DOWN) {
5154 mPreventDrag = msg.arg2 == 1;
5155 if (mPreventDrag) {
5156 mTouchMode = TOUCH_DONE_MODE;
5157 }
5158 }
5159 break;
5160
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005161 case REQUEST_KEYBOARD:
5162 if (msg.arg1 == 0) {
5163 hideSoftKeyboard();
5164 } else {
5165 displaySoftKeyboard(false);
5166 }
5167 break;
5168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005169 default:
5170 super.handleMessage(msg);
5171 break;
5172 }
5173 }
5174 }
5175
5176 // Class used to use a dropdown for a <select> element
5177 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005178 // Whether the listbox allows multiple selection.
5179 private boolean mMultiple;
5180 // Passed in to a list with multiple selection to tell
5181 // which items are selected.
5182 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04005183 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005184 // where the initial selection is.
5185 private int mSelection;
5186
5187 private Container[] mContainers;
5188
5189 // Need these to provide stable ids to my ArrayAdapter,
5190 // which normally does not have stable ids. (Bug 1250098)
5191 private class Container extends Object {
5192 String mString;
5193 boolean mEnabled;
5194 int mId;
5195
5196 public String toString() {
5197 return mString;
5198 }
5199 }
5200
5201 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04005202 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005203 * and allow filtering.
5204 */
5205 private class MyArrayListAdapter extends ArrayAdapter<Container> {
5206 public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005207 super(context,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005208 multiple ? com.android.internal.R.layout.select_dialog_multichoice :
Cary Clarkd6982c92009-05-29 11:02:22 -04005209 com.android.internal.R.layout.select_dialog_singlechoice,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005210 objects);
5211 }
5212
5213 @Override
5214 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04005215 // AdapterView's onChanged method uses this to determine whether
5216 // to restore the old state. Return false so that the old (out
5217 // of date) state does not replace the new, valid state.
5218 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005219 }
5220
5221 private Container item(int position) {
5222 if (position < 0 || position >= getCount()) {
5223 return null;
5224 }
5225 return (Container) getItem(position);
5226 }
5227
5228 @Override
5229 public long getItemId(int position) {
5230 Container item = item(position);
5231 if (item == null) {
5232 return -1;
5233 }
5234 return item.mId;
5235 }
5236
5237 @Override
5238 public boolean areAllItemsEnabled() {
5239 return false;
5240 }
5241
5242 @Override
5243 public boolean isEnabled(int position) {
5244 Container item = item(position);
5245 if (item == null) {
5246 return false;
5247 }
5248 return item.mEnabled;
5249 }
5250 }
5251
5252 private InvokeListBox(String[] array,
5253 boolean[] enabled, int[] selected) {
5254 mMultiple = true;
5255 mSelectedArray = selected;
5256
5257 int length = array.length;
5258 mContainers = new Container[length];
5259 for (int i = 0; i < length; i++) {
5260 mContainers[i] = new Container();
5261 mContainers[i].mString = array[i];
5262 mContainers[i].mEnabled = enabled[i];
5263 mContainers[i].mId = i;
5264 }
5265 }
5266
Cary Clarkd6982c92009-05-29 11:02:22 -04005267 private InvokeListBox(String[] array, boolean[] enabled, int
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005268 selection) {
5269 mSelection = selection;
5270 mMultiple = false;
5271
5272 int length = array.length;
5273 mContainers = new Container[length];
5274 for (int i = 0; i < length; i++) {
5275 mContainers[i] = new Container();
5276 mContainers[i].mString = array[i];
5277 mContainers[i].mEnabled = enabled[i];
5278 mContainers[i].mId = i;
5279 }
5280 }
5281
Leon Scroggins3667ce42009-05-13 15:58:03 -04005282 /*
5283 * Whenever the data set changes due to filtering, this class ensures
5284 * that the checked item remains checked.
5285 */
5286 private class SingleDataSetObserver extends DataSetObserver {
5287 private long mCheckedId;
5288 private ListView mListView;
5289 private Adapter mAdapter;
5290
5291 /*
5292 * Create a new observer.
5293 * @param id The ID of the item to keep checked.
5294 * @param l ListView for getting and clearing the checked states
5295 * @param a Adapter for getting the IDs
5296 */
5297 public SingleDataSetObserver(long id, ListView l, Adapter a) {
5298 mCheckedId = id;
5299 mListView = l;
5300 mAdapter = a;
5301 }
5302
5303 public void onChanged() {
5304 // The filter may have changed which item is checked. Find the
5305 // item that the ListView thinks is checked.
5306 int position = mListView.getCheckedItemPosition();
5307 long id = mAdapter.getItemId(position);
5308 if (mCheckedId != id) {
5309 // Clear the ListView's idea of the checked item, since
5310 // it is incorrect
5311 mListView.clearChoices();
5312 // Search for mCheckedId. If it is in the filtered list,
5313 // mark it as checked
5314 int count = mAdapter.getCount();
5315 for (int i = 0; i < count; i++) {
5316 if (mAdapter.getItemId(i) == mCheckedId) {
5317 mListView.setItemChecked(i, true);
5318 break;
5319 }
5320 }
5321 }
5322 }
5323
5324 public void onInvalidate() {}
5325 }
5326
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005327 public void run() {
5328 final ListView listView = (ListView) LayoutInflater.from(mContext)
5329 .inflate(com.android.internal.R.layout.select_dialog, null);
Cary Clarkd6982c92009-05-29 11:02:22 -04005330 final MyArrayListAdapter adapter = new
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005331 MyArrayListAdapter(mContext, mContainers, mMultiple);
5332 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
5333 .setView(listView).setCancelable(true)
5334 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04005335
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005336 if (mMultiple) {
5337 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
5338 public void onClick(DialogInterface dialog, int which) {
5339 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04005340 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005341 adapter.getCount(), 0,
5342 listView.getCheckedItemPositions());
5343 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07005344 b.setNegativeButton(android.R.string.cancel,
5345 new DialogInterface.OnClickListener() {
5346 public void onClick(DialogInterface dialog, int which) {
5347 mWebViewCore.sendMessage(
5348 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5349 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005350 }
5351 final AlertDialog dialog = b.create();
5352 listView.setAdapter(adapter);
5353 listView.setFocusableInTouchMode(true);
5354 // There is a bug (1250103) where the checks in a ListView with
5355 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04005356 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005357 // filtered. Do not allow filtering on multiple lists until
5358 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04005359
Leon Scroggins3667ce42009-05-13 15:58:03 -04005360 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005361 if (mMultiple) {
5362 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
5363 int length = mSelectedArray.length;
5364 for (int i = 0; i < length; i++) {
5365 listView.setItemChecked(mSelectedArray[i], true);
5366 }
5367 } else {
5368 listView.setOnItemClickListener(new OnItemClickListener() {
5369 public void onItemClick(AdapterView parent, View v,
5370 int position, long id) {
5371 mWebViewCore.sendMessage(
5372 EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
5373 dialog.dismiss();
5374 }
5375 });
5376 if (mSelection != -1) {
5377 listView.setSelection(mSelection);
5378 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
5379 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04005380 DataSetObserver observer = new SingleDataSetObserver(
5381 adapter.getItemId(mSelection), listView, adapter);
5382 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005383 }
5384 }
5385 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
5386 public void onCancel(DialogInterface dialog) {
5387 mWebViewCore.sendMessage(
5388 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5389 }
5390 });
5391 dialog.show();
5392 }
5393 }
5394
5395 /*
5396 * Request a dropdown menu for a listbox with multiple selection.
5397 *
5398 * @param array Labels for the listbox.
5399 * @param enabledArray Which positions are enabled.
5400 * @param selectedArray Which positions are initally selected.
5401 */
5402 void requestListBox(String[] array, boolean[]enabledArray, int[]
5403 selectedArray) {
5404 mPrivateHandler.post(
5405 new InvokeListBox(array, enabledArray, selectedArray));
5406 }
5407
5408 /*
5409 * Request a dropdown menu for a listbox with single selection or a single
5410 * <select> element.
5411 *
5412 * @param array Labels for the listbox.
5413 * @param enabledArray Which positions are enabled.
5414 * @param selection Which position is initally selected.
5415 */
5416 void requestListBox(String[] array, boolean[]enabledArray, int selection) {
5417 mPrivateHandler.post(
5418 new InvokeListBox(array, enabledArray, selection));
5419 }
5420
5421 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005422 private void sendMoveMouse(int frame, int node, int x, int y) {
5423 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
5424 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005425 }
5426
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005427 /*
5428 * Send a mouse move event to the webcore thread.
5429 *
5430 * @param removeFocus Pass true if the "mouse" cursor is now over a node
5431 * which wants key events, but it is not the focus. This
5432 * will make the visual appear as though nothing is in
5433 * focus. Remove the WebTextView, if present, and stop
5434 * drawing the blinking caret.
5435 * called by JNI
5436 */
5437 private void sendMoveMouseIfLatest(boolean removeFocus) {
5438 if (removeFocus) {
5439 clearTextEntry();
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04005440 setFocusControllerInactive();
Cary Clark19436562009-06-04 16:25:07 -04005441 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005442 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
5443 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005444 }
5445
5446 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005447 private void sendMotionUp(int touchGeneration,
5448 int frame, int node, int x, int y, int size) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005449 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
5450 touchUpData.mMoveGeneration = touchGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005451 touchUpData.mSize = size;
Cary Clarkd6982c92009-05-29 11:02:22 -04005452 touchUpData.mFrame = frame;
5453 touchUpData.mNode = node;
5454 touchUpData.mX = x;
5455 touchUpData.mY = y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005456 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
5457 }
5458
5459
5460 private int getScaledMaxXScroll() {
5461 int width;
5462 if (mHeightCanMeasure == false) {
5463 width = getViewWidth() / 4;
5464 } else {
5465 Rect visRect = new Rect();
5466 calcOurVisibleRect(visRect);
5467 width = visRect.width() / 2;
5468 }
5469 // FIXME the divisor should be retrieved from somewhere
5470 return viewToContent(width);
5471 }
5472
5473 private int getScaledMaxYScroll() {
5474 int height;
5475 if (mHeightCanMeasure == false) {
5476 height = getViewHeight() / 4;
5477 } else {
5478 Rect visRect = new Rect();
5479 calcOurVisibleRect(visRect);
5480 height = visRect.height() / 2;
5481 }
5482 // FIXME the divisor should be retrieved from somewhere
5483 // the closest thing today is hard-coded into ScrollView.java
5484 // (from ScrollView.java, line 363) int maxJump = height/2;
5485 return viewToContent(height);
5486 }
5487
5488 /**
5489 * Called by JNI to invalidate view
5490 */
5491 private void viewInvalidate() {
5492 invalidate();
5493 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005494
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005495 // return true if the key was handled
Cary Clark215b72c2009-06-26 14:38:43 -04005496 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
5497 long time, boolean ignorePlugin) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005498 if (mNativeClass == 0) {
5499 return false;
5500 }
Cary Clark215b72c2009-06-26 14:38:43 -04005501 if (ignorePlugin == false && nativePluginEatsNavKey()) {
5502 KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
5503 , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
5504 | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
5505 | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
5506 , 0, 0, 0);
5507 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
5508 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
5509 return true;
5510 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005511 mLastCursorTime = time;
5512 mLastCursorBounds = nativeGetCursorRingBounds();
5513 boolean keyHandled
5514 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005515 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005516 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
5517 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005518 + " handled=" + keyHandled);
5519 }
5520 if (keyHandled == false || mHeightCanMeasure == false) {
5521 return keyHandled;
5522 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005523 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
5524 if (contentCursorRingBounds.isEmpty()) return keyHandled;
5525 Rect viewCursorRingBounds = contentToView(contentCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005526 Rect visRect = new Rect();
5527 calcOurVisibleRect(visRect);
5528 Rect outset = new Rect(visRect);
5529 int maxXScroll = visRect.width() / 2;
5530 int maxYScroll = visRect.height() / 2;
5531 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005532 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005533 return keyHandled;
5534 }
5535 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005536 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
5537 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005538 if (maxH > 0) {
5539 pinScrollBy(maxH, 0, true, 0);
5540 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005541 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
5542 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005543 if (maxH < 0) {
5544 pinScrollBy(maxH, 0, true, 0);
5545 }
5546 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005547 if (mLastCursorBounds.isEmpty()) return keyHandled;
5548 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
5549 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005550 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005551 if (DebugFlags.WEB_VIEW) {
5552 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
5553 + contentCursorRingBounds);
5554 }
5555 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005556 mUserScroll = true;
5557 return keyHandled;
5558 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005559
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005560 /**
5561 * Set the background color. It's white by default. Pass
5562 * zero to make the view transparent.
5563 * @param color the ARGB color described by Color.java
5564 */
5565 public void setBackgroundColor(int color) {
5566 mBackgroundColor = color;
5567 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
5568 }
5569
5570 public void debugDump() {
5571 nativeDebugDump();
5572 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
5573 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005575 /**
5576 * Update our cache with updatedText.
5577 * @param updatedText The new text to put in our cache.
5578 */
5579 /* package */ void updateCachedTextfield(String updatedText) {
5580 // Also place our generation number so that when we look at the cache
5581 // we recognize that it is up to date.
5582 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
5583 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005584
Leon Scroggins4890feb2009-07-02 10:37:10 -04005585 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005586 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04005587 private native int nativeCursorFramePointer();
5588 private native Rect nativeCursorNodeBounds();
5589 /* package */ native int nativeCursorNodePointer();
5590 /* package */ native boolean nativeCursorMatchesFocus();
5591 private native boolean nativeCursorIntersects(Rect visibleRect);
5592 private native boolean nativeCursorIsAnchor();
Cary Clark215b72c2009-06-26 14:38:43 -04005593 private native boolean nativeCursorIsPlugin();
Cary Clarkd6982c92009-05-29 11:02:22 -04005594 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04005595 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04005596 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005597 /**
5598 * Returns true if the native cursor node says it wants to handle key events
5599 * (ala plugins). This can only be called if mNativeClass is non-zero!
5600 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04005601 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005602 private native void nativeDebugDump();
5603 private native void nativeDestroy();
Cary Clarkd6982c92009-05-29 11:02:22 -04005604 private native void nativeDrawCursorRing(Canvas content);
5605 private native void nativeDrawMatches(Canvas canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005606 private native void nativeDrawSelection(Canvas content
5607 , int x, int y, boolean extendSelection);
5608 private native void nativeDrawSelectionRegion(Canvas content);
Cary Clarkd6982c92009-05-29 11:02:22 -04005609 private native void nativeDumpDisplayTree(String urlOrNull);
5610 private native int nativeFindAll(String findLower, String findUpper);
5611 private native void nativeFindNext(boolean forward);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005612 private native boolean nativeFocusCandidateIsPassword();
5613 private native boolean nativeFocusCandidateIsRtlText();
5614 private native boolean nativeFocusCandidateIsTextField();
5615 private native boolean nativeFocusCandidateIsTextInput();
5616 private native int nativeFocusCandidateMaxLength();
Leon Scroggins0ca70882009-06-26 17:45:29 -04005617 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005618 private native Rect nativeFocusCandidateNodeBounds();
5619 /* package */ native int nativeFocusCandidatePointer();
5620 private native String nativeFocusCandidateText();
5621 private native int nativeFocusCandidateTextSize();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005622 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04005623 private native Rect nativeGetCursorRingBounds();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005624 private native Region nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04005625 private native boolean nativeHasCursorNode();
5626 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04005627 private native void nativeHideCursor();
Cary Clarkd6982c92009-05-29 11:02:22 -04005628 private native String nativeImageURI(int x, int y);
5629 private native void nativeInstrumentReport();
Cary Clarkd6982c92009-05-29 11:02:22 -04005630 // return true if the page has been scrolled
5631 private native boolean nativeMotionUp(int x, int y, int slop);
5632 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005633 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04005634 boolean noScroll);
5635 private native int nativeMoveGeneration();
5636 private native void nativeMoveSelection(int x, int y,
5637 boolean extendSelection);
Cary Clark215b72c2009-06-26 14:38:43 -04005638 private native boolean nativePluginEatsNavKey();
Cary Clarkd6982c92009-05-29 11:02:22 -04005639 // Like many other of our native methods, you must make sure that
5640 // mNativeClass is not null before calling this method.
5641 private native void nativeRecordButtons(boolean focused,
5642 boolean pressed, boolean invalidate);
5643 private native void nativeSelectBestAt(Rect rect);
5644 private native void nativeSetFindIsDown();
5645 private native void nativeSetFollowedLink(boolean followed);
5646 private native void nativeSetHeightCanMeasure(boolean measure);
5647 private native int nativeTextGeneration();
5648 // Never call this version except by updateCachedTextfield(String) -
5649 // we always want to pass in our generation number.
5650 private native void nativeUpdateCachedTextfield(String updatedText,
5651 int generation);
Cary Clark215b72c2009-06-26 14:38:43 -04005652 private native void nativeUpdatePluginReceivesEvents();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005653 // return NO_LEFTEDGE means failure.
5654 private static final int NO_LEFTEDGE = -1;
5655 private native int nativeGetBlockLeftEdge(int x, int y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005656}