blob: d99f9f2bf17773b2a3a4937074e122618ff64ae9 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.app.AlertDialog;
20import android.content.Context;
21import android.content.DialogInterface;
22import android.content.Intent;
23import android.content.DialogInterface.OnCancelListener;
Leon Scroggins3667ce42009-05-13 15:58:03 -040024import android.database.DataSetObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.Color;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.graphics.Picture;
29import android.graphics.Point;
30import android.graphics.Rect;
31import android.graphics.Region;
Mike Reede8853fc2009-09-04 14:01:48 -040032import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.net.http.SslCertificate;
34import android.net.Uri;
35import android.os.Bundle;
36import android.os.Handler;
37import android.os.Message;
38import android.os.ServiceManager;
39import android.os.SystemClock;
40import android.provider.Checkin;
41import android.text.IClipboard;
42import android.text.Selection;
43import android.text.Spannable;
44import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.util.EventLog;
46import android.util.Log;
Leon Scroggins3a6c88c2009-09-09 14:50:23 -040047import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.view.Gravity;
49import android.view.KeyEvent;
50import android.view.LayoutInflater;
51import android.view.MotionEvent;
52import android.view.SoundEffectConstants;
53import android.view.VelocityTracker;
54import android.view.View;
55import android.view.ViewConfiguration;
56import android.view.ViewGroup;
57import android.view.ViewParent;
58import android.view.ViewTreeObserver;
The Android Open Source Project10592532009-03-18 17:39:46 -070059import android.view.animation.AlphaAnimation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.view.inputmethod.InputMethodManager;
Leon Scrogginsd3465f62009-06-02 10:57:54 -040061import android.webkit.WebTextView.AutoCompleteAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.webkit.WebViewCore.EventHub;
63import android.widget.AbsoluteLayout;
Leon Scroggins3667ce42009-05-13 15:58:03 -040064import android.widget.Adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import android.widget.AdapterView;
66import android.widget.ArrayAdapter;
Leon Scrogginsa8da1732009-10-19 19:04:30 -040067import android.widget.CheckedTextView;
The Android Open Source Project10592532009-03-18 17:39:46 -070068import android.widget.FrameLayout;
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040069import android.widget.LinearLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071import android.widget.Scroller;
72import android.widget.Toast;
73import android.widget.ZoomButtonsController;
The Android Open Source Project10592532009-03-18 17:39:46 -070074import android.widget.ZoomControls;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075import android.widget.AdapterView.OnItemClickListener;
76
77import java.io.File;
78import java.io.FileInputStream;
79import java.io.FileNotFoundException;
80import java.io.FileOutputStream;
81import java.io.IOException;
82import java.net.URLDecoder;
83import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import java.util.List;
Andrei Popescu4950b2b2009-09-03 13:56:07 +010085import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040087import junit.framework.Assert;
88
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089/**
Cary Clarkd6982c92009-05-29 11:02:22 -040090 * <p>A View that displays web pages. This class is the basis upon which you
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 * can roll your own web browser or simply display some online content within your Activity.
92 * It uses the WebKit rendering engine to display
93 * web pages and includes methods to navigate forward and backward
94 * through a history, zoom in and out, perform text searches and more.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -070095 * <p>To enable the built-in zoom, set
96 * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
97 * (introduced in API version 3).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098 * <p>Note that, in order for your Activity to access the Internet and load web pages
Cary Clarkd6982c92009-05-29 11:02:22 -040099 * in a WebView, you must add the <var>INTERNET</var> permissions to your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 * Android Manifest file:</p>
101 * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100102 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 * <p>This must be a child of the <code>&lt;manifest></code> element.</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100104 *
105 * <h3>Basic usage</h3>
106 *
107 * <p>By default, a WebView provides no browser-like widgets, does not
108 * enable JavaScript and errors will be ignored. If your goal is only
109 * to display some HTML as a part of your UI, this is probably fine;
110 * the user won't need to interact with the web page beyond reading
111 * it, and the web page won't need to interact with the user. If you
112 * actually want a fully blown web browser, then you probably want to
113 * invoke the Browser application with your URL rather than show it
114 * with a WebView. See {@link android.content.Intent} for more information.</p>
115 *
116 * <pre class="prettyprint">
117 * WebView webview = new WebView(this);
118 * setContentView(webview);
119 *
120 * // Simplest usage: note that an exception will NOT be thrown
121 * // if there is an error loading this page (see below).
122 * webview.loadUrl("http://slashdot.org/");
123 *
124 * // Of course you can also load from any string:
125 * String summary = "&lt;html>&lt;body>You scored &lt;b>192</b> points.&lt;/body>&lt;/html>";
126 * webview.loadData(summary, "text/html", "utf-8");
127 * // ... although note that there are restrictions on what this HTML can do.
128 * // See the JavaDocs for loadData and loadDataWithBaseUrl for more info.
129 * </pre>
130 *
131 * <p>A WebView has several customization points where you can add your
132 * own behavior. These are:</p>
133 *
134 * <ul>
135 * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
136 * This class is called when something that might impact a
137 * browser UI happens, for instance, progress updates and
138 * JavaScript alerts are sent here.
139 * </li>
140 * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
141 * It will be called when things happen that impact the
142 * rendering of the content, eg, errors or form submissions. You
143 * can also intercept URL loading here.</li>
144 * <li>Via the {@link android.webkit.WebSettings} class, which contains
145 * miscellaneous configuration. </li>
146 * <li>With the {@link android.webkit.WebView#addJavascriptInterface} method.
147 * This lets you bind Java objects into the WebView so they can be
148 * controlled from the web pages JavaScript.</li>
149 * </ul>
150 *
151 * <p>Here's a more complicated example, showing error handling,
152 * settings, and progress notification:</p>
153 *
154 * <pre class="prettyprint">
155 * // Let's display the progress in the activity title bar, like the
156 * // browser app does.
157 * getWindow().requestFeature(Window.FEATURE_PROGRESS);
158 *
159 * webview.getSettings().setJavaScriptEnabled(true);
160 *
161 * final Activity activity = this;
162 * webview.setWebChromeClient(new WebChromeClient() {
163 * public void onProgressChanged(WebView view, int progress) {
164 * // Activities and WebViews measure progress with different scales.
165 * // The progress meter will automatically disappear when we reach 100%
166 * activity.setProgress(progress * 1000);
167 * }
168 * });
169 * webview.setWebViewClient(new WebViewClient() {
170 * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
171 * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
172 * }
173 * });
174 *
175 * webview.loadUrl("http://slashdot.org/");
176 * </pre>
177 *
178 * <h3>Cookie and window management</h3>
179 *
180 * <p>For obvious security reasons, your application has its own
181 * cache, cookie store etc - it does not share the Browser
182 * applications data. Cookies are managed on a separate thread, so
183 * operations like index building don't block the UI
184 * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
185 * if you want to use cookies in your application.
186 * </p>
187 *
188 * <p>By default, requests by the HTML to open new windows are
189 * ignored. This is true whether they be opened by JavaScript or by
190 * the target attribute on a link. You can customize your
191 * WebChromeClient to provide your own behaviour for opening multiple windows,
192 * and render them in whatever manner you want.</p>
193 *
194 * <p>Standard behavior for an Activity is to be destroyed and
195 * recreated when the devices orientation is changed. This will cause
196 * the WebView to reload the current page. If you don't want that, you
197 * can set your Activity to handle the orientation and keyboardHidden
198 * changes, and then just leave the WebView alone. It'll automatically
199 * re-orient itself as appropriate.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800200 */
Cary Clarkd6982c92009-05-29 11:02:22 -0400201public class WebView extends AbsoluteLayout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800202 implements ViewTreeObserver.OnGlobalFocusChangeListener,
203 ViewGroup.OnHierarchyChangeListener {
204
205 // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
206 // the screen all-the-time. Good for profiling our drawing code
207 static private final boolean AUTO_REDRAW_HACK = false;
208 // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
209 private boolean mAutoRedraw;
210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 static final String LOGTAG = "webview";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212
Leon Scroggins213a31c2009-05-21 16:49:32 -0700213 private static class ExtendedZoomControls extends FrameLayout {
The Android Open Source Project10592532009-03-18 17:39:46 -0700214 public ExtendedZoomControls(Context context, AttributeSet attrs) {
215 super(context, attrs);
216 LayoutInflater inflater = (LayoutInflater)
217 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
218 inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400219 mPlusMinusZoomControls = (ZoomControls) findViewById(
220 com.android.internal.R.id.zoomControls);
Grace Kloba04b28682009-09-14 14:38:37 -0700221 findViewById(com.android.internal.R.id.zoomMagnify).setVisibility(
222 View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700223 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400224
The Android Open Source Project10592532009-03-18 17:39:46 -0700225 public void show(boolean showZoom, boolean canZoomOut) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400226 mPlusMinusZoomControls.setVisibility(
227 showZoom ? View.VISIBLE : View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700228 fade(View.VISIBLE, 0.0f, 1.0f);
229 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400230
The Android Open Source Project10592532009-03-18 17:39:46 -0700231 public void hide() {
232 fade(View.GONE, 1.0f, 0.0f);
233 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400234
The Android Open Source Project10592532009-03-18 17:39:46 -0700235 private void fade(int visibility, float startAlpha, float endAlpha) {
236 AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
237 anim.setDuration(500);
238 startAnimation(anim);
239 setVisibility(visibility);
240 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400241
The Android Open Source Project10592532009-03-18 17:39:46 -0700242 public boolean hasFocus() {
Grace Kloba04b28682009-09-14 14:38:37 -0700243 return mPlusMinusZoomControls.hasFocus();
The Android Open Source Project10592532009-03-18 17:39:46 -0700244 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400245
The Android Open Source Project10592532009-03-18 17:39:46 -0700246 public void setOnZoomInClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400247 mPlusMinusZoomControls.setOnZoomInClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700248 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400249
The Android Open Source Project10592532009-03-18 17:39:46 -0700250 public void setOnZoomOutClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400251 mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700252 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400253
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400254 ZoomControls mPlusMinusZoomControls;
The Android Open Source Project10592532009-03-18 17:39:46 -0700255 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400256
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257 /**
258 * Transportation object for returning WebView across thread boundaries.
259 */
260 public class WebViewTransport {
261 private WebView mWebview;
262
263 /**
264 * Set the WebView to the transportation object.
265 * @param webview The WebView to transport.
266 */
267 public synchronized void setWebView(WebView webview) {
268 mWebview = webview;
269 }
270
271 /**
272 * Return the WebView object.
273 * @return WebView The transported WebView object.
274 */
275 public synchronized WebView getWebView() {
276 return mWebview;
277 }
278 }
279
280 // A final CallbackProxy shared by WebViewCore and BrowserFrame.
281 private final CallbackProxy mCallbackProxy;
282
283 private final WebViewDatabase mDatabase;
284
285 // SSL certificate for the main top-level page (if secure)
286 private SslCertificate mCertificate;
287
288 // Native WebView pointer that is 0 until the native object has been
289 // created.
290 private int mNativeClass;
291 // This would be final but it needs to be set to null when the WebView is
292 // destroyed.
293 private WebViewCore mWebViewCore;
294 // Handler for dispatching UI messages.
295 /* package */ final Handler mPrivateHandler = new PrivateHandler();
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400296 private WebTextView mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 // Used to ignore changes to webkit text that arrives to the UI side after
298 // more key events.
299 private int mTextGeneration;
300
Patrick Scott0a5ce012009-07-02 08:56:10 -0400301 // Used by WebViewCore to create child views.
302 /* package */ final ViewManager mViewManager;
303
Grace Kloba11438c32009-12-16 11:39:12 -0800304 // Used to display in full screen mode
305 PluginFullScreenHolder mFullScreenHolder;
306
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 /**
308 * Position of the last touch event.
309 */
310 private float mLastTouchX;
311 private float mLastTouchY;
312
313 /**
314 * Time of the last touch event.
315 */
316 private long mLastTouchTime;
317
318 /**
319 * Time of the last time sending touch event to WebViewCore
320 */
321 private long mLastSentTouchTime;
322
323 /**
324 * The minimum elapsed time before sending another ACTION_MOVE event to
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700325 * WebViewCore. This really should be tuned for each type of the devices.
326 * For example in Google Map api test case, it takes Dream device at least
327 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
328 * triggering the layout and drawing the picture. While the same process
329 * takes 60+ms on the current high speed device. If we make
330 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
331 * to WebViewCore queue and the real layout and draw events will be pushed
332 * to further, which slows down the refresh rate. Choose 50 to favor the
333 * current high speed devices. For Dream like devices, 100 is a better
334 * choice. Maybe make this in the buildspec later.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800335 */
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700336 private static final int TOUCH_SENT_INTERVAL = 50;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337
338 /**
339 * Helper class to get velocity for fling
340 */
341 VelocityTracker mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700342 private int mMaximumFling;
Cary Clark278ce052009-08-31 16:08:42 -0400343 private float mLastVelocity;
344 private float mLastVelX;
345 private float mLastVelY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 /**
348 * Touch mode
349 */
350 private int mTouchMode = TOUCH_DONE_MODE;
351 private static final int TOUCH_INIT_MODE = 1;
352 private static final int TOUCH_DRAG_START_MODE = 2;
353 private static final int TOUCH_DRAG_MODE = 3;
354 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
355 private static final int TOUCH_SHORTPRESS_MODE = 5;
Grace Kloba04b28682009-09-14 14:38:37 -0700356 private static final int TOUCH_DOUBLE_TAP_MODE = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 private static final int TOUCH_DONE_MODE = 7;
358 private static final int TOUCH_SELECT_MODE = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359
360 // Whether to forward the touch events to WebCore
361 private boolean mForwardTouchEvents = false;
362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 // Whether to prevent drag during touch. The initial value depends on
364 // mForwardTouchEvents. If WebCore wants touch events, we assume it will
365 // take control of touch events unless it says no for touch down event.
Grace Klobaf58af622009-09-24 17:41:23 -0700366 private static final int PREVENT_DRAG_NO = 0;
367 private static final int PREVENT_DRAG_MAYBE_YES = 1;
368 private static final int PREVENT_DRAG_YES = 2;
369 private int mPreventDrag = PREVENT_DRAG_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370
Grace Kloba5f68d6f2009-12-08 18:42:54 -0800371 // by default mPreventLongPress is false. If it is true, long press event
372 // will be handled by WebKit instead of UI.
373 private boolean mPreventLongPress = false;
374 // by default mPreventDoubleTap is false. If it is true, double tap event
375 // will be handled by WebKit instead of UI.
376 private boolean mPreventDoubleTap = false;
377
378 // this needs to be in sync with the logic in WebKit's
379 // EventHandler::handleTouchEvent()
380 private static final int TOUCH_PREVENT_DRAG = 0x1;
381 private static final int TOUCH_PREVENT_LONGPRESS = 0x2;
382 private static final int TOUCH_PREVENT_DOUBLETAP = 0x4;
383
Leon Scroggins72543e12009-07-23 15:29:45 -0400384 // To keep track of whether the current drag was initiated by a WebTextView,
385 // so that we know not to hide the cursor
386 boolean mDragFromTextInput;
387
Cary Clarkd6982c92009-05-29 11:02:22 -0400388 // Whether or not to draw the cursor ring.
389 private boolean mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800390
Mike Reedd205d5b2009-05-27 11:02:29 -0400391 // true if onPause has been called (and not onResume)
392 private boolean mIsPaused;
393
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 /**
395 * Customizable constant
396 */
397 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
398 private int mTouchSlopSquare;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700399 // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
400 private int mDoubleTapSlopSquare;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700401 // pre-computed density adjusted navigation slop
402 private int mNavSlop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 // This should be ViewConfiguration.getTapTimeout()
404 // But system time out is 100ms, which is too short for the browser.
405 // In the browser, if it switches out of tap too soon, jump tap won't work.
406 private static final int TAP_TIMEOUT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 // This should be ViewConfiguration.getLongPressTimeout()
408 // But system time out is 500ms, which is too short for the browser.
409 // With a short timeout, it's difficult to treat trigger a short press.
410 private static final int LONG_PRESS_TIMEOUT = 1000;
411 // needed to avoid flinging after a pause of no movement
412 private static final int MIN_FLING_TIME = 250;
Cary Clark25415e22009-10-12 13:41:28 -0400413 // draw unfiltered after drag is held without movement
414 private static final int MOTIONLESS_TIME = 100;
The Android Open Source Project10592532009-03-18 17:39:46 -0700415 // The time that the Zoom Controls are visible before fading away
Cary Clarkd6982c92009-05-29 11:02:22 -0400416 private static final long ZOOM_CONTROLS_TIMEOUT =
The Android Open Source Project10592532009-03-18 17:39:46 -0700417 ViewConfiguration.getZoomControlsTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800418 // The amount of content to overlap between two screens when going through
419 // pages with the space bar, in pixels.
420 private static final int PAGE_SCROLL_OVERLAP = 24;
421
422 /**
423 * These prevent calling requestLayout if either dimension is fixed. This
424 * depends on the layout parameters and the measure specs.
425 */
426 boolean mWidthCanMeasure;
427 boolean mHeightCanMeasure;
428
429 // Remember the last dimensions we sent to the native side so we can avoid
430 // sending the same dimensions more than once.
431 int mLastWidthSent;
432 int mLastHeightSent;
433
434 private int mContentWidth; // cache of value from WebViewCore
435 private int mContentHeight; // cache of value from WebViewCore
436
Cary Clarkd6982c92009-05-29 11:02:22 -0400437 // Need to have the separate control for horizontal and vertical scrollbar
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 // style than the View's single scrollbar style
439 private boolean mOverlayHorizontalScrollbar = true;
440 private boolean mOverlayVerticalScrollbar = false;
441
442 // our standard speed. this way small distances will be traversed in less
443 // time than large distances, but we cap the duration, so that very large
444 // distances won't take too long to get there.
445 private static final int STD_SPEED = 480; // pixels per second
446 // time for the longest scroll animation
447 private static final int MAX_DURATION = 750; // milliseconds
Leon Scroggins03c87bf2009-09-18 15:05:59 -0400448 private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800449 private Scroller mScroller;
450
451 private boolean mWrapContent;
Cary Clark25415e22009-10-12 13:41:28 -0400452 private static final int MOTIONLESS_FALSE = 0;
453 private static final int MOTIONLESS_PENDING = 1;
454 private static final int MOTIONLESS_TRUE = 2;
455 private int mHeldMotionless;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 /**
458 * Private message ids
459 */
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400460 private static final int REMEMBER_PASSWORD = 1;
461 private static final int NEVER_REMEMBER_PASSWORD = 2;
462 private static final int SWITCH_TO_SHORTPRESS = 3;
463 private static final int SWITCH_TO_LONGPRESS = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700464 private static final int RELEASE_SINGLE_TAP = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400465 private static final int REQUEST_FORM_DATA = 6;
Grace Klobadd817492009-09-14 10:17:06 -0700466 private static final int RESUME_WEBCORE_UPDATE = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400467 private static final int DRAG_HELD_MOTIONLESS = 8;
468 private static final int AWAKEN_SCROLL_BARS = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800469
470 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400471 static final int SCROLL_TO_MSG_ID = 10;
472 static final int SCROLL_BY_MSG_ID = 11;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800473 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400474 static final int SPAWN_SCROLL_TO_MSG_ID = 12;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400476 static final int SYNC_SCROLL_TO_MSG_ID = 13;
477 static final int NEW_PICTURE_MSG_ID = 14;
478 static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
479 static final int WEBCORE_INITIALIZED_MSG_ID = 16;
480 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
Leon Scroggins5de63892009-10-29 09:48:43 -0400481 static final int FIND_AGAIN = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400482 static final int MOVE_OUT_OF_PLUGIN = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400483 static final int CLEAR_TEXT_ENTRY = 20;
Leon Scroggins6679f2f2009-08-12 18:48:10 -0400484 static final int UPDATE_TEXT_SELECTION_MSG_ID = 21;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400485 static final int UPDATE_CLIPBOARD = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400486 static final int LONG_PRESS_CENTER = 23;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400487 static final int PREVENT_TOUCH_ID = 24;
488 static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 // obj=Rect in doc coordinates
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400490 static final int INVAL_RECT_MSG_ID = 26;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400491 static final int REQUEST_KEYBOARD = 27;
Cary Clark1cb97ee2009-12-11 12:10:36 -0500492 static final int DO_MOTION_UP = 28;
Grace Kloba11438c32009-12-16 11:39:12 -0800493 static final int SHOW_FULLSCREEN = 29;
494 static final int HIDE_FULLSCREEN = 30;
Cary Clarkd6982c92009-05-29 11:02:22 -0400495
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 static final String[] HandlerDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400497 "REMEMBER_PASSWORD", // = 1;
498 "NEVER_REMEMBER_PASSWORD", // = 2;
499 "SWITCH_TO_SHORTPRESS", // = 3;
500 "SWITCH_TO_LONGPRESS", // = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700501 "RELEASE_SINGLE_TAP", // = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400502 "REQUEST_FORM_DATA", // = 6;
Cary Clark25415e22009-10-12 13:41:28 -0400503 "RESUME_WEBCORE_UPDATE", // = 7;
504 "DRAG_HELD_MOTIONLESS", // = 8;
505 "AWAKEN_SCROLL_BARS", // = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800506 "SCROLL_TO_MSG_ID", // = 10;
507 "SCROLL_BY_MSG_ID", // = 11;
508 "SPAWN_SCROLL_TO_MSG_ID", // = 12;
509 "SYNC_SCROLL_TO_MSG_ID", // = 13;
510 "NEW_PICTURE_MSG_ID", // = 14;
511 "UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
512 "WEBCORE_INITIALIZED_MSG_ID", // = 16;
513 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
Leon Scroggins5de63892009-10-29 09:48:43 -0400514 "FIND_AGAIN", // = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400515 "MOVE_OUT_OF_PLUGIN", // = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400516 "CLEAR_TEXT_ENTRY", // = 20;
Leon Scroggins6679f2f2009-08-12 18:48:10 -0400517 "UPDATE_TEXT_SELECTION_MSG_ID", // = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 "UPDATE_CLIPBOARD", // = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400519 "LONG_PRESS_CENTER", // = 23;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800520 "PREVENT_TOUCH_ID", // = 24;
521 "WEBCORE_NEED_TOUCH_EVENTS", // = 25;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400522 "INVAL_RECT_MSG_ID", // = 26;
Cary Clark1cb97ee2009-12-11 12:10:36 -0500523 "REQUEST_KEYBOARD", // = 27;
Grace Kloba11438c32009-12-16 11:39:12 -0800524 "DO_MOTION_UP", // = 28;
525 "SHOW_FULLSCREEN", // = 29;
526 "HIDE_FULLSCREEN" // = 30;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 };
528
Grace Klobaa4fa1072009-11-09 12:01:50 -0800529 // If the site doesn't use the viewport meta tag to specify the viewport,
530 // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
531 static final int DEFAULT_VIEWPORT_WIDTH = 800;
532
533 // normally we try to fit the content to the minimum preferred width
534 // calculated by the Webkit. To avoid the bad behavior when some site's
535 // minimum preferred width keeps growing when changing the viewport width or
536 // the minimum preferred width is huge, an upper limit is needed.
537 static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
538
Grace Kloba25737912009-06-19 12:42:47 -0700539 // default scale limit. Depending on the display density
540 private static float DEFAULT_MAX_ZOOM_SCALE;
541 private static float DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800542 // scale limit, which can be set through viewport meta tag in the web page
Grace Kloba25737912009-06-19 12:42:47 -0700543 private float mMaxZoomScale;
544 private float mMinZoomScale;
Grace Klobae397a882009-08-06 12:04:14 -0700545 private boolean mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546
547 // initial scale in percent. 0 means using default.
Grace Kloba16efce72009-11-10 15:49:03 -0800548 private int mInitialScaleInPercent = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800549
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700550 // while in the zoom overview mode, the page's width is fully fit to the
551 // current window. The page is alive, in another words, you can click to
552 // follow the links. Double tap will toggle between zoom overview mode and
553 // the last zoom scale.
554 boolean mInZoomOverview = false;
Leon Scrogginsf58ffac2009-08-13 15:34:06 -0400555
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700556 // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
557 // engadget always have wider mContentWidth no matter what viewport size is.
Grace Klobaa4fa1072009-11-09 12:01:50 -0800558 int mZoomOverviewWidth = DEFAULT_VIEWPORT_WIDTH;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700559 float mLastScale;
560
Grace Kloba25737912009-06-19 12:42:47 -0700561 // default scale. Depending on the display density.
562 static int DEFAULT_SCALE_PERCENT;
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700563 private float mDefaultScale;
Grace Kloba25737912009-06-19 12:42:47 -0700564
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800565 // set to true temporarily while the zoom control is being dragged
566 private boolean mPreviewZoomOnly = false;
567
568 // computed scale and inverse, from mZoomWidth.
Grace Kloba25737912009-06-19 12:42:47 -0700569 private float mActualScale;
570 private float mInvActualScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800571 // if this is non-zero, it is used on drawing rather than mActualScale
572 private float mZoomScale;
573 private float mInvInitialZoomScale;
574 private float mInvFinalZoomScale;
Grace Kloba675c7d22009-07-23 09:21:21 -0700575 private int mInitialScrollX;
576 private int mInitialScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577 private long mZoomStart;
578 private static final int ZOOM_ANIMATION_LENGTH = 500;
579
580 private boolean mUserScroll = false;
581
582 private int mSnapScrollMode = SNAP_NONE;
Cary Clarkac492e12009-10-14 14:53:37 -0400583 private static final int SNAP_NONE = 0;
584 private static final int SNAP_LOCK = 1; // not a separate state
585 private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
586 private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400588
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 // Used to match key downs and key ups
590 private boolean mGotKeyDown;
591
592 /* package */ static boolean mLogEvent = true;
593 private static final int EVENT_LOG_ZOOM_LEVEL_CHANGE = 70101;
594 private static final int EVENT_LOG_DOUBLE_TAP_DURATION = 70102;
595
596 // for event log
597 private long mLastTouchUpTime = 0;
598
599 /**
600 * URI scheme for telephone number
601 */
602 public static final String SCHEME_TEL = "tel:";
603 /**
604 * URI scheme for email address
605 */
606 public static final String SCHEME_MAILTO = "mailto:";
607 /**
608 * URI scheme for map address
609 */
610 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400611
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 private int mBackgroundColor = Color.WHITE;
613
614 // Used to notify listeners of a new picture.
615 private PictureListener mPictureListener;
616 /**
617 * Interface to listen for new pictures as they change.
618 */
619 public interface PictureListener {
620 /**
621 * Notify the listener that the picture has changed.
622 * @param view The WebView that owns the picture.
623 * @param picture The new picture.
624 */
625 public void onNewPicture(WebView view, Picture picture);
626 }
627
Leon Scroggins3246c222009-05-26 09:51:23 -0400628 // FIXME: Want to make this public, but need to change the API file.
629 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 /**
631 * Default HitTestResult, where the target is unknown
632 */
633 public static final int UNKNOWN_TYPE = 0;
634 /**
635 * HitTestResult for hitting a HTML::a tag
636 */
637 public static final int ANCHOR_TYPE = 1;
638 /**
639 * HitTestResult for hitting a phone number
640 */
641 public static final int PHONE_TYPE = 2;
642 /**
643 * HitTestResult for hitting a map address
644 */
645 public static final int GEO_TYPE = 3;
646 /**
647 * HitTestResult for hitting an email address
648 */
649 public static final int EMAIL_TYPE = 4;
650 /**
651 * HitTestResult for hitting an HTML::img tag
652 */
653 public static final int IMAGE_TYPE = 5;
654 /**
655 * HitTestResult for hitting a HTML::a tag which contains HTML::img
656 */
657 public static final int IMAGE_ANCHOR_TYPE = 6;
658 /**
659 * HitTestResult for hitting a HTML::a tag with src=http
660 */
661 public static final int SRC_ANCHOR_TYPE = 7;
662 /**
663 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
664 */
665 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
666 /**
667 * HitTestResult for hitting an edit text area
668 */
669 public static final int EDIT_TEXT_TYPE = 9;
670
671 private int mType;
672 private String mExtra;
673
674 HitTestResult() {
675 mType = UNKNOWN_TYPE;
676 }
677
678 private void setType(int type) {
679 mType = type;
680 }
681
682 private void setExtra(String extra) {
683 mExtra = extra;
684 }
685
686 public int getType() {
687 return mType;
688 }
689
690 public String getExtra() {
691 return mExtra;
692 }
693 }
694
The Android Open Source Project10592532009-03-18 17:39:46 -0700695 // The View containing the zoom controls
696 private ExtendedZoomControls mZoomControls;
697 private Runnable mZoomControlRunnable;
698
Cary Clarkd6982c92009-05-29 11:02:22 -0400699 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700701 // These keep track of the center point of the zoom. They are used to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 // determine the point around which we should zoom.
703 private float mZoomCenterX;
704 private float mZoomCenterY;
705
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700706 private ZoomButtonsController.OnZoomListener mZoomListener =
707 new ZoomButtonsController.OnZoomListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800709 public void onVisibilityChanged(boolean visible) {
710 if (visible) {
711 switchOutDrawHistory();
Mike Reed8b302092009-11-12 12:50:20 -0500712 // Bring back the hidden zoom controls.
713 mZoomButtonsController.getZoomControls().setVisibility(
714 View.VISIBLE);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700715 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 }
717 }
718
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700719 public void onZoom(boolean zoomIn) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 if (zoomIn) {
721 zoomIn();
722 } else {
723 zoomOut();
724 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400725
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700726 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800727 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 };
Cary Clarkd6982c92009-05-29 11:02:22 -0400729
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800730 /**
731 * Construct a new WebView with a Context object.
732 * @param context A Context object used to access application assets.
733 */
734 public WebView(Context context) {
735 this(context, null);
736 }
737
738 /**
739 * Construct a new WebView with layout parameters.
740 * @param context A Context object used to access application assets.
741 * @param attrs An AttributeSet passed to our parent.
742 */
743 public WebView(Context context, AttributeSet attrs) {
744 this(context, attrs, com.android.internal.R.attr.webViewStyle);
745 }
746
747 /**
748 * Construct a new WebView with layout parameters and a default style.
749 * @param context A Context object used to access application assets.
750 * @param attrs An AttributeSet passed to our parent.
751 * @param defStyle The default style resource ID.
752 */
753 public WebView(Context context, AttributeSet attrs, int defStyle) {
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100754 this(context, attrs, defStyle, null);
755 }
756
757 /**
758 * Construct a new WebView with layout parameters, a default style and a set
759 * of custom Javscript interfaces to be added to the WebView at initialization
Romain Guy01d0fbf2009-12-01 14:52:19 -0800760 * time. This guarantees that these interfaces will be available when the JS
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100761 * context is initialized.
762 * @param context A Context object used to access application assets.
763 * @param attrs An AttributeSet passed to our parent.
764 * @param defStyle The default style resource ID.
765 * @param javascriptInterfaces is a Map of intareface names, as keys, and
766 * object implementing those interfaces, as values.
767 * @hide pending API council approval.
768 */
769 protected WebView(Context context, AttributeSet attrs, int defStyle,
770 Map<String, Object> javascriptInterfaces) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800771 super(context, attrs, defStyle);
772 init();
773
774 mCallbackProxy = new CallbackProxy(context, this);
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100775 mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 mDatabase = WebViewDatabase.getInstance(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 mScroller = new Scroller(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700778
Patrick Scott0a5ce012009-07-02 08:56:10 -0400779 mViewManager = new ViewManager(this);
780
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700781 mZoomButtonsController = new ZoomButtonsController(this);
782 mZoomButtonsController.setOnZoomListener(mZoomListener);
Leon Scrogginsaa3f96a2009-06-11 14:46:35 -0400783 // ZoomButtonsController positions the buttons at the bottom, but in
784 // the middle. Change their layout parameters so they appear on the
785 // right.
786 View controls = mZoomButtonsController.getZoomControls();
787 ViewGroup.LayoutParams params = controls.getLayoutParams();
788 if (params instanceof FrameLayout.LayoutParams) {
789 FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
790 params;
791 frameParams.gravity = Gravity.RIGHT;
792 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700793 }
794
795 private void updateZoomButtonsEnabled() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700796 boolean canZoomIn = mActualScale < mMaxZoomScale;
Grace Kloba455e3af2009-08-13 11:01:21 -0700797 boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview;
The Android Open Source Project10592532009-03-18 17:39:46 -0700798 if (!canZoomIn && !canZoomOut) {
799 // Hide the zoom in and out buttons, as well as the fit to page
800 // button, if the page cannot zoom
801 mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700802 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -0700803 // Set each one individually, as a page may be able to zoom in
804 // or out.
805 mZoomButtonsController.setZoomInEnabled(canZoomIn);
806 mZoomButtonsController.setZoomOutEnabled(canZoomOut);
The Android Open Source Project10592532009-03-18 17:39:46 -0700807 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 }
809
810 private void init() {
811 setWillNotDraw(false);
812 setFocusable(true);
813 setFocusableInTouchMode(true);
814 setClickable(true);
815 setLongClickable(true);
816
Romain Guy4296fc42009-07-06 11:48:52 -0700817 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700818 int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819 mTouchSlopSquare = slop * slop;
820 mMinLockSnapReverseDistance = slop;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700821 slop = configuration.getScaledDoubleTapSlop();
822 mDoubleTapSlopSquare = slop * slop;
Grace Kloba25737912009-06-19 12:42:47 -0700823 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700824 // use one line height, 16 based on our current default font, for how
825 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -0700826 mNavSlop = (int) (16 * density);
827 // density adjusted scale factors
828 DEFAULT_SCALE_PERCENT = (int) (100 * density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700829 mDefaultScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700830 mActualScale = density;
831 mInvActualScale = 1 / density;
832 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
833 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
834 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
835 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
Romain Guy4296fc42009-07-06 11:48:52 -0700836 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 }
838
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700839 /* package */void updateDefaultZoomDensity(int zoomDensity) {
840 final float density = getContext().getResources().getDisplayMetrics().density
841 * 100 / zoomDensity;
842 if (Math.abs(density - mDefaultScale) > 0.01) {
843 float scaleFactor = density / mDefaultScale;
844 // adjust the limits
845 mNavSlop = (int) (16 * density);
846 DEFAULT_SCALE_PERCENT = (int) (100 * density);
847 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
848 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
849 mDefaultScale = density;
850 mMaxZoomScale *= scaleFactor;
851 mMinZoomScale *= scaleFactor;
852 setNewZoomScale(mActualScale * scaleFactor, false);
853 }
854 }
855
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 /* package */ boolean onSavePassword(String schemePlusHost, String username,
857 String password, final Message resumeMsg) {
858 boolean rVal = false;
859 if (resumeMsg == null) {
860 // null resumeMsg implies saving password silently
861 mDatabase.setUsernamePassword(schemePlusHost, username, password);
862 } else {
863 final Message remember = mPrivateHandler.obtainMessage(
864 REMEMBER_PASSWORD);
865 remember.getData().putString("host", schemePlusHost);
866 remember.getData().putString("username", username);
867 remember.getData().putString("password", password);
868 remember.obj = resumeMsg;
869
870 final Message neverRemember = mPrivateHandler.obtainMessage(
871 NEVER_REMEMBER_PASSWORD);
872 neverRemember.getData().putString("host", schemePlusHost);
873 neverRemember.getData().putString("username", username);
874 neverRemember.getData().putString("password", password);
875 neverRemember.obj = resumeMsg;
876
877 new AlertDialog.Builder(getContext())
878 .setTitle(com.android.internal.R.string.save_password_label)
879 .setMessage(com.android.internal.R.string.save_password_message)
880 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
881 new DialogInterface.OnClickListener() {
882 public void onClick(DialogInterface dialog, int which) {
883 resumeMsg.sendToTarget();
884 }
885 })
886 .setNeutralButton(com.android.internal.R.string.save_password_remember,
887 new DialogInterface.OnClickListener() {
888 public void onClick(DialogInterface dialog, int which) {
889 remember.sendToTarget();
890 }
891 })
892 .setNegativeButton(com.android.internal.R.string.save_password_never,
893 new DialogInterface.OnClickListener() {
894 public void onClick(DialogInterface dialog, int which) {
895 neverRemember.sendToTarget();
896 }
897 })
898 .setOnCancelListener(new OnCancelListener() {
899 public void onCancel(DialogInterface dialog) {
900 resumeMsg.sendToTarget();
901 }
902 }).show();
903 // Return true so that WebViewCore will pause while the dialog is
904 // up.
905 rVal = true;
906 }
907 return rVal;
908 }
909
910 @Override
911 public void setScrollBarStyle(int style) {
912 if (style == View.SCROLLBARS_INSIDE_INSET
913 || style == View.SCROLLBARS_OUTSIDE_INSET) {
914 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
915 } else {
916 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
917 }
918 super.setScrollBarStyle(style);
919 }
920
921 /**
922 * Specify whether the horizontal scrollbar has overlay style.
923 * @param overlay TRUE if horizontal scrollbar should have overlay style.
924 */
925 public void setHorizontalScrollbarOverlay(boolean overlay) {
926 mOverlayHorizontalScrollbar = overlay;
927 }
928
929 /**
930 * Specify whether the vertical scrollbar has overlay style.
931 * @param overlay TRUE if vertical scrollbar should have overlay style.
932 */
933 public void setVerticalScrollbarOverlay(boolean overlay) {
934 mOverlayVerticalScrollbar = overlay;
935 }
936
937 /**
938 * Return whether horizontal scrollbar has overlay style
939 * @return TRUE if horizontal scrollbar has overlay style.
940 */
941 public boolean overlayHorizontalScrollbar() {
942 return mOverlayHorizontalScrollbar;
943 }
944
945 /**
946 * Return whether vertical scrollbar has overlay style
947 * @return TRUE if vertical scrollbar has overlay style.
948 */
949 public boolean overlayVerticalScrollbar() {
950 return mOverlayVerticalScrollbar;
951 }
952
953 /*
954 * Return the width of the view where the content of WebView should render
955 * to.
Grace Kloba6ed525e2009-09-17 15:31:12 -0700956 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 */
Grace Kloba6ed525e2009-09-17 15:31:12 -0700958 /* package */ int getViewWidth() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800959 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
960 return getWidth();
961 } else {
962 return getWidth() - getVerticalScrollbarWidth();
963 }
964 }
965
966 /*
Mike Reede8853fc2009-09-04 14:01:48 -0400967 * returns the height of the titlebarview (if any). Does not care about
968 * scrolling
969 */
970 private int getTitleHeight() {
971 return mTitleBar != null ? mTitleBar.getHeight() : 0;
972 }
973
974 /*
975 * Return the amount of the titlebarview (if any) that is visible
976 */
977 private int getVisibleTitleHeight() {
978 return Math.max(getTitleHeight() - mScrollY, 0);
979 }
980
981 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 * Return the height of the view where the content of WebView should render
Leon Scroggins0236e672009-09-02 21:12:08 -0400983 * to. Note that this excludes mTitleBar, if there is one.
Grace Kloba6ed525e2009-09-17 15:31:12 -0700984 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 */
Grace Kloba6ed525e2009-09-17 15:31:12 -0700986 /* package */ int getViewHeight() {
Grace Kloba8eff73f2009-09-24 09:34:32 -0700987 return getViewHeightWithTitle() - getVisibleTitleHeight();
988 }
989
990 private int getViewHeightWithTitle() {
Leon Scroggins0236e672009-09-02 21:12:08 -0400991 int height = getHeight();
Mike Reede8853fc2009-09-04 14:01:48 -0400992 if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
Leon Scroggins0236e672009-09-02 21:12:08 -0400993 height -= getHorizontalScrollbarHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 }
Grace Kloba8eff73f2009-09-24 09:34:32 -0700995 return height;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 }
997
998 /**
999 * @return The SSL certificate for the main top-level page or null if
1000 * there is no certificate (the site is not secure).
1001 */
1002 public SslCertificate getCertificate() {
1003 return mCertificate;
1004 }
1005
1006 /**
1007 * Sets the SSL certificate for the main top-level page.
1008 */
1009 public void setCertificate(SslCertificate certificate) {
1010 // here, the certificate can be null (if the site is not secure)
1011 mCertificate = certificate;
1012 }
1013
1014 //-------------------------------------------------------------------------
1015 // Methods called by activity
1016 //-------------------------------------------------------------------------
1017
1018 /**
1019 * Save the username and password for a particular host in the WebView's
1020 * internal database.
1021 * @param host The host that required the credentials.
1022 * @param username The username for the given host.
1023 * @param password The password for the given host.
1024 */
1025 public void savePassword(String host, String username, String password) {
1026 mDatabase.setUsernamePassword(host, username, password);
1027 }
1028
1029 /**
1030 * Set the HTTP authentication credentials for a given host and realm.
1031 *
1032 * @param host The host for the credentials.
1033 * @param realm The realm for the credentials.
1034 * @param username The username for the password. If it is null, it means
1035 * password can't be saved.
1036 * @param password The password
1037 */
1038 public void setHttpAuthUsernamePassword(String host, String realm,
1039 String username, String password) {
1040 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
1041 }
1042
1043 /**
1044 * Retrieve the HTTP authentication username and password for a given
1045 * host & realm pair
1046 *
1047 * @param host The host for which the credentials apply.
1048 * @param realm The realm for which the credentials apply.
1049 * @return String[] if found, String[0] is username, which can be null and
1050 * String[1] is password. Return null if it can't find anything.
1051 */
1052 public String[] getHttpAuthUsernamePassword(String host, String realm) {
1053 return mDatabase.getHttpAuthUsernamePassword(host, realm);
1054 }
1055
1056 /**
1057 * Destroy the internal state of the WebView. This method should be called
1058 * after the WebView has been removed from the view system. No other
1059 * methods may be called on a WebView after destroy.
1060 */
1061 public void destroy() {
1062 clearTextEntry();
1063 if (mWebViewCore != null) {
1064 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -04001065 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 mCallbackProxy.setWebViewClient(null);
1067 mCallbackProxy.setWebChromeClient(null);
1068 // Tell WebViewCore to destroy itself
1069 WebViewCore webViewCore = mWebViewCore;
1070 mWebViewCore = null; // prevent using partial webViewCore
1071 webViewCore.destroy();
1072 // Remove any pending messages that might not be serviced yet.
1073 mPrivateHandler.removeCallbacksAndMessages(null);
1074 mCallbackProxy.removeCallbacksAndMessages(null);
1075 // Wake up the WebCore thread just in case it is waiting for a
1076 // javascript dialog.
1077 synchronized (mCallbackProxy) {
1078 mCallbackProxy.notify();
1079 }
1080 }
1081 if (mNativeClass != 0) {
1082 nativeDestroy();
1083 mNativeClass = 0;
1084 }
1085 }
1086
1087 /**
1088 * Enables platform notifications of data state and proxy changes.
1089 */
1090 public static void enablePlatformNotifications() {
1091 Network.enablePlatformNotifications();
1092 }
1093
1094 /**
1095 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001096 * from the Activity's onPause() or onStop().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 */
1098 public static void disablePlatformNotifications() {
1099 Network.disablePlatformNotifications();
1100 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 /**
Feng Qianb3081372009-06-29 15:55:18 -07001103 * Sets JavaScript engine flags.
1104 *
1105 * @param flags JS engine flags in a String
1106 *
1107 * @hide pending API solidification
1108 */
1109 public void setJsFlags(String flags) {
1110 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1111 }
1112
1113 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 * Inform WebView of the network state. This is used to set
1115 * the javascript property window.navigator.isOnline and
1116 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1117 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 */
1119 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001120 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1121 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 }
1123
1124 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001125 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1127 * method no longer stores the display data for this WebView. The previous
1128 * behavior could potentially leak files if {@link #restoreState} was never
1129 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1130 * and restoring the display data.
1131 * @param outState The Bundle to store the WebView state.
1132 * @return The same copy of the back/forward list used to save the state. If
1133 * saveState fails, the returned list will be null.
1134 * @see #savePicture
1135 * @see #restorePicture
1136 */
1137 public WebBackForwardList saveState(Bundle outState) {
1138 if (outState == null) {
1139 return null;
1140 }
1141 // We grab a copy of the back/forward list because a client of WebView
1142 // may have invalidated the history list by calling clearHistory.
1143 WebBackForwardList list = copyBackForwardList();
1144 final int currentIndex = list.getCurrentIndex();
1145 final int size = list.getSize();
1146 // We should fail saving the state if the list is empty or the index is
1147 // not in a valid range.
1148 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1149 return null;
1150 }
1151 outState.putInt("index", currentIndex);
1152 // FIXME: This should just be a byte[][] instead of ArrayList but
1153 // Parcel.java does not have the code to handle multi-dimensional
1154 // arrays.
1155 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1156 for (int i = 0; i < size; i++) {
1157 WebHistoryItem item = list.getItemAtIndex(i);
Cary Clark4fbf81b2009-09-29 16:07:56 -04001158 if (null == item) {
1159 // FIXME: this shouldn't happen
1160 // need to determine how item got set to null
1161 Log.w(LOGTAG, "saveState: Unexpected null history item.");
1162 return null;
1163 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 byte[] data = item.getFlattenedData();
1165 if (data == null) {
1166 // It would be very odd to not have any data for a given history
1167 // item. And we will fail to rebuild the history list without
1168 // flattened data.
1169 return null;
1170 }
1171 history.add(data);
1172 }
1173 outState.putSerializable("history", history);
1174 if (mCertificate != null) {
1175 outState.putBundle("certificate",
1176 SslCertificate.saveState(mCertificate));
1177 }
1178 return list;
1179 }
1180
1181 /**
1182 * Save the current display data to the Bundle given. Used in conjunction
1183 * with {@link #saveState}.
1184 * @param b A Bundle to store the display data.
1185 * @param dest The file to store the serialized picture data. Will be
1186 * overwritten with this WebView's picture data.
1187 * @return True if the picture was successfully saved.
1188 */
1189 public boolean savePicture(Bundle b, File dest) {
1190 if (dest == null || b == null) {
1191 return false;
1192 }
1193 final Picture p = capturePicture();
1194 try {
1195 final FileOutputStream out = new FileOutputStream(dest);
1196 p.writeToStream(out);
1197 out.close();
1198 } catch (FileNotFoundException e){
1199 e.printStackTrace();
1200 } catch (IOException e) {
1201 e.printStackTrace();
1202 } catch (RuntimeException e) {
1203 e.printStackTrace();
1204 }
1205 if (dest.length() > 0) {
1206 b.putInt("scrollX", mScrollX);
1207 b.putInt("scrollY", mScrollY);
1208 b.putFloat("scale", mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001209 if (mInZoomOverview) {
1210 b.putFloat("lastScale", mLastScale);
1211 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 return true;
1213 }
1214 return false;
1215 }
1216
1217 /**
1218 * Restore the display data that was save in {@link #savePicture}. Used in
1219 * conjunction with {@link #restoreState}.
1220 * @param b A Bundle containing the saved display data.
1221 * @param src The file where the picture data was stored.
1222 * @return True if the picture was successfully restored.
1223 */
1224 public boolean restorePicture(Bundle b, File src) {
1225 if (src == null || b == null) {
1226 return false;
1227 }
1228 if (src.exists()) {
1229 Picture p = null;
1230 try {
1231 final FileInputStream in = new FileInputStream(src);
1232 p = Picture.createFromStream(in);
1233 in.close();
1234 } catch (FileNotFoundException e){
1235 e.printStackTrace();
1236 } catch (RuntimeException e) {
1237 e.printStackTrace();
1238 } catch (IOException e) {
1239 e.printStackTrace();
1240 }
1241 if (p != null) {
1242 int sx = b.getInt("scrollX", 0);
1243 int sy = b.getInt("scrollY", 0);
1244 float scale = b.getFloat("scale", 1.0f);
1245 mDrawHistory = true;
1246 mHistoryPicture = p;
1247 mScrollX = sx;
1248 mScrollY = sy;
1249 mHistoryWidth = Math.round(p.getWidth() * scale);
1250 mHistoryHeight = Math.round(p.getHeight() * scale);
1251 // as getWidth() / getHeight() of the view are not
1252 // available yet, set up mActualScale, so that when
1253 // onSizeChanged() is called, the rest will be set
1254 // correctly
1255 mActualScale = scale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001256 float lastScale = b.getFloat("lastScale", -1.0f);
1257 if (lastScale > 0) {
1258 mInZoomOverview = true;
1259 mLastScale = lastScale;
1260 } else {
1261 mInZoomOverview = false;
1262 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 invalidate();
1264 return true;
1265 }
1266 }
1267 return false;
1268 }
1269
1270 /**
1271 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001272 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1273 * be called to restore the state of the WebView before using the object. If
1274 * it is called after the WebView has had a chance to build state (load
1275 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 * side-effects. Please note that this method no longer restores the
1277 * display data for this WebView. See {@link #savePicture} and {@link
1278 * #restorePicture} for saving and restoring the display data.
1279 * @param inState The incoming Bundle of state.
1280 * @return The restored back/forward list or null if restoreState failed.
1281 * @see #savePicture
1282 * @see #restorePicture
1283 */
1284 public WebBackForwardList restoreState(Bundle inState) {
1285 WebBackForwardList returnList = null;
1286 if (inState == null) {
1287 return returnList;
1288 }
1289 if (inState.containsKey("index") && inState.containsKey("history")) {
1290 mCertificate = SslCertificate.restoreState(
1291 inState.getBundle("certificate"));
1292
1293 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1294 final int index = inState.getInt("index");
1295 // We can't use a clone of the list because we need to modify the
1296 // shared copy, so synchronize instead to prevent concurrent
1297 // modifications.
1298 synchronized (list) {
1299 final List<byte[]> history =
1300 (List<byte[]>) inState.getSerializable("history");
1301 final int size = history.size();
1302 // Check the index bounds so we don't crash in native code while
1303 // restoring the history index.
1304 if (index < 0 || index >= size) {
1305 return null;
1306 }
1307 for (int i = 0; i < size; i++) {
1308 byte[] data = history.remove(0);
1309 if (data == null) {
1310 // If we somehow have null data, we cannot reconstruct
1311 // the item and thus our history list cannot be rebuilt.
1312 return null;
1313 }
1314 WebHistoryItem item = new WebHistoryItem(data);
1315 list.addHistoryItem(item);
1316 }
1317 // Grab the most recent copy to return to the caller.
1318 returnList = copyBackForwardList();
1319 // Update the copy to have the correct index.
1320 returnList.setCurrentIndex(index);
1321 }
1322 // Remove all pending messages because we are restoring previous
1323 // state.
1324 mWebViewCore.removeMessages();
1325 // Send a restore state message.
1326 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1327 }
1328 return returnList;
1329 }
1330
1331 /**
1332 * Load the given url.
1333 * @param url The url of the resource to load.
1334 */
1335 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001336 if (url == null) {
1337 return;
1338 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001339 switchOutDrawHistory();
1340 mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
1341 clearTextEntry();
1342 }
1343
1344 /**
Grace Kloba57534302009-05-22 18:55:02 -07001345 * Load the url with postData using "POST" method into the WebView. If url
1346 * is not a network url, it will be loaded with {link
1347 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001348 *
Grace Kloba57534302009-05-22 18:55:02 -07001349 * @param url The url of the resource to load.
1350 * @param postData The data will be passed to "POST" request.
Grace Kloba57534302009-05-22 18:55:02 -07001351 */
1352 public void postUrl(String url, byte[] postData) {
1353 if (URLUtil.isNetworkUrl(url)) {
1354 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001355 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1356 arg.mUrl = url;
1357 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001358 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
1359 clearTextEntry();
1360 } else {
1361 loadUrl(url);
1362 }
1363 }
1364
1365 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366 * Load the given data into the WebView. This will load the data into
1367 * WebView using the data: scheme. Content loaded through this mechanism
1368 * does not have the ability to load content from the network.
1369 * @param data A String of data in the given encoding.
1370 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1371 * @param encoding The encoding of the data. i.e. utf-8, base64
1372 */
1373 public void loadData(String data, String mimeType, String encoding) {
1374 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1375 }
1376
1377 /**
1378 * Load the given data into the WebView, use the provided URL as the base
1379 * URL for the content. The base URL is the URL that represents the page
1380 * that is loaded through this interface. As such, it is used for the
1381 * history entry and to resolve any relative URLs. The failUrl is used if
1382 * browser fails to load the data provided. If it is empty or null, and the
1383 * load fails, then no history entry is created.
1384 * <p>
1385 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1386 * files through "file:///android_asset/" for the sub resources is more
1387 * restricted. If you provide null or empty string as baseUrl, you won't be
1388 * able to access asset files. If the baseUrl is anything other than
1389 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1390 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001391 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392 * @param baseUrl Url to resolve relative paths with, if null defaults to
1393 * "about:blank"
1394 * @param data A String of data in the given encoding.
1395 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1396 * defaults to "text/html"
1397 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
1398 * @param failUrl URL to use if the content fails to load or null.
1399 */
1400 public void loadDataWithBaseURL(String baseUrl, String data,
1401 String mimeType, String encoding, String failUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001402
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1404 loadData(data, mimeType, encoding);
1405 return;
1406 }
1407 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001408 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1409 arg.mBaseUrl = baseUrl;
1410 arg.mData = data;
1411 arg.mMimeType = mimeType;
1412 arg.mEncoding = encoding;
1413 arg.mFailUrl = failUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001414 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
1415 clearTextEntry();
1416 }
1417
1418 /**
1419 * Stop the current load.
1420 */
1421 public void stopLoading() {
1422 // TODO: should we clear all the messages in the queue before sending
1423 // STOP_LOADING?
1424 switchOutDrawHistory();
1425 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1426 }
1427
1428 /**
1429 * Reload the current url.
1430 */
1431 public void reload() {
Leon Scroggins74077c82009-09-14 18:56:48 -04001432 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001433 switchOutDrawHistory();
1434 mWebViewCore.sendMessage(EventHub.RELOAD);
1435 }
1436
1437 /**
1438 * Return true if this WebView has a back history item.
1439 * @return True iff this WebView has a back history item.
1440 */
1441 public boolean canGoBack() {
1442 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1443 synchronized (l) {
1444 if (l.getClearPending()) {
1445 return false;
1446 } else {
1447 return l.getCurrentIndex() > 0;
1448 }
1449 }
1450 }
1451
1452 /**
1453 * Go back in the history of this WebView.
1454 */
1455 public void goBack() {
1456 goBackOrForward(-1);
1457 }
1458
1459 /**
1460 * Return true if this WebView has a forward history item.
1461 * @return True iff this Webview has a forward history item.
1462 */
1463 public boolean canGoForward() {
1464 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1465 synchronized (l) {
1466 if (l.getClearPending()) {
1467 return false;
1468 } else {
1469 return l.getCurrentIndex() < l.getSize() - 1;
1470 }
1471 }
1472 }
1473
1474 /**
1475 * Go forward in the history of this WebView.
1476 */
1477 public void goForward() {
1478 goBackOrForward(1);
1479 }
1480
1481 /**
1482 * Return true if the page can go back or forward the given
1483 * number of steps.
1484 * @param steps The negative or positive number of steps to move the
1485 * history.
1486 */
1487 public boolean canGoBackOrForward(int steps) {
1488 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1489 synchronized (l) {
1490 if (l.getClearPending()) {
1491 return false;
1492 } else {
1493 int newIndex = l.getCurrentIndex() + steps;
1494 return newIndex >= 0 && newIndex < l.getSize();
1495 }
1496 }
1497 }
1498
1499 /**
1500 * Go to the history item that is the number of steps away from
1501 * the current item. Steps is negative if backward and positive
1502 * if forward.
1503 * @param steps The number of steps to take back or forward in the back
1504 * forward list.
1505 */
1506 public void goBackOrForward(int steps) {
1507 goBackOrForward(steps, false);
1508 }
1509
1510 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
1511 // every time we go back or forward, we want to reset the
1512 // WebView certificate:
1513 // if the new site is secure, we will reload it and get a
1514 // new certificate set;
1515 // if the new site is not secure, the certificate must be
1516 // null, and that will be the case
1517 mCertificate = null;
1518 if (steps != 0) {
1519 clearTextEntry();
1520 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
1521 ignoreSnapshot ? 1 : 0);
1522 }
1523 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001524
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 private boolean extendScroll(int y) {
1526 int finalY = mScroller.getFinalY();
1527 int newY = pinLocY(finalY + y);
1528 if (newY == finalY) return false;
1529 mScroller.setFinalY(newY);
1530 mScroller.extendDuration(computeDuration(0, y));
1531 return true;
1532 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001533
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001534 /**
1535 * Scroll the contents of the view up by half the view size
1536 * @param top true to jump to the top of the page
1537 * @return true if the page was scrolled
1538 */
1539 public boolean pageUp(boolean top) {
1540 if (mNativeClass == 0) {
1541 return false;
1542 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001543 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001544 if (top) {
1545 // go to the top of the document
1546 return pinScrollTo(mScrollX, 0, true, 0);
1547 }
1548 // Page up
1549 int h = getHeight();
1550 int y;
1551 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1552 y = -h + PAGE_SCROLL_OVERLAP;
1553 } else {
1554 y = -h / 2;
1555 }
1556 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001557 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001558 : extendScroll(y);
1559 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001560
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001561 /**
1562 * Scroll the contents of the view down by half the page size
1563 * @param bottom true to jump to bottom of page
1564 * @return true if the page was scrolled
1565 */
1566 public boolean pageDown(boolean bottom) {
1567 if (mNativeClass == 0) {
1568 return false;
1569 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001570 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001571 if (bottom) {
Grace Klobad7439f42009-11-10 14:11:33 -08001572 return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001573 }
1574 // Page down.
1575 int h = getHeight();
1576 int y;
1577 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1578 y = h - PAGE_SCROLL_OVERLAP;
1579 } else {
1580 y = h / 2;
1581 }
1582 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001583 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001584 : extendScroll(y);
1585 }
1586
1587 /**
1588 * Clear the view so that onDraw() will draw nothing but white background,
1589 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
1590 */
1591 public void clearView() {
1592 mContentWidth = 0;
1593 mContentHeight = 0;
1594 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
1595 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001596
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597 /**
1598 * Return a new picture that captures the current display of the webview.
1599 * This is a copy of the display, and will be unaffected if the webview
1600 * later loads a different URL.
1601 *
1602 * @return a picture containing the current contents of the view. Note this
1603 * picture is of the entire document, and is not restricted to the
1604 * bounds of the view.
1605 */
1606 public Picture capturePicture() {
Cary Clarkd6982c92009-05-29 11:02:22 -04001607 if (null == mWebViewCore) return null; // check for out of memory tab
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001608 return mWebViewCore.copyContentPicture();
1609 }
1610
1611 /**
1612 * Return true if the browser is displaying a TextView for text input.
1613 */
1614 private boolean inEditingMode() {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001615 return mWebTextView != null && mWebTextView.getParent() != null
1616 && mWebTextView.hasFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001617 }
1618
1619 private void clearTextEntry() {
1620 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001621 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001622 }
1623 }
1624
Cary Clarkd6982c92009-05-29 11:02:22 -04001625 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001626 * Return the current scale of the WebView
1627 * @return The current scale.
1628 */
1629 public float getScale() {
1630 return mActualScale;
1631 }
1632
1633 /**
1634 * Set the initial scale for the WebView. 0 means default. If
1635 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
1636 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
1637 * WebView starts will this value as initial scale.
1638 *
1639 * @param scaleInPercent The initial scale in percent.
1640 */
1641 public void setInitialScale(int scaleInPercent) {
Grace Kloba16efce72009-11-10 15:49:03 -08001642 mInitialScaleInPercent = scaleInPercent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 }
1644
1645 /**
1646 * Invoke the graphical zoom picker widget for this WebView. This will
1647 * result in the zoom widget appearing on the screen to control the zoom
1648 * level of this WebView.
1649 */
1650 public void invokeZoomPicker() {
1651 if (!getSettings().supportZoom()) {
1652 Log.w(LOGTAG, "This WebView doesn't support zoom.");
1653 return;
1654 }
1655 clearTextEntry();
The Android Open Source Project10592532009-03-18 17:39:46 -07001656 if (getSettings().getBuiltInZoomControls()) {
1657 mZoomButtonsController.setVisible(true);
1658 } else {
1659 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
1660 mPrivateHandler.postDelayed(mZoomControlRunnable,
1661 ZOOM_CONTROLS_TIMEOUT);
1662 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001663 }
1664
1665 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001666 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001667 * is found and the anchor has a non-javascript url, the HitTestResult type
1668 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
1669 * anchor does not have a url or if it is a javascript url, the type will
1670 * be UNKNOWN_TYPE and the url has to be retrieved through
1671 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
1672 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
1673 * the "extra" field. A type of
1674 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
1675 * a child node. If a phone number is found, the HitTestResult type is set
1676 * to PHONE_TYPE and the phone number is set in the "extra" field of
1677 * HitTestResult. If a map address is found, the HitTestResult type is set
1678 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
1679 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
1680 * and the email is set in the "extra" field of HitTestResult. Otherwise,
1681 * HitTestResult type is set to UNKNOWN_TYPE.
1682 */
1683 public HitTestResult getHitTestResult() {
1684 if (mNativeClass == 0) {
1685 return null;
1686 }
1687
1688 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04001689 if (nativeHasCursorNode()) {
1690 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001691 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04001692 } else {
1693 String text = nativeCursorText();
1694 if (text != null) {
1695 if (text.startsWith(SCHEME_TEL)) {
1696 result.setType(HitTestResult.PHONE_TYPE);
1697 result.setExtra(text.substring(SCHEME_TEL.length()));
1698 } else if (text.startsWith(SCHEME_MAILTO)) {
1699 result.setType(HitTestResult.EMAIL_TYPE);
1700 result.setExtra(text.substring(SCHEME_MAILTO.length()));
1701 } else if (text.startsWith(SCHEME_GEO)) {
1702 result.setType(HitTestResult.GEO_TYPE);
1703 result.setExtra(URLDecoder.decode(text
1704 .substring(SCHEME_GEO.length())));
1705 } else if (nativeCursorIsAnchor()) {
1706 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
1707 result.setExtra(text);
1708 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001709 }
1710 }
1711 }
1712 int type = result.getType();
1713 if (type == HitTestResult.UNKNOWN_TYPE
1714 || type == HitTestResult.SRC_ANCHOR_TYPE) {
1715 // Now check to see if it is an image.
Leon Scroggins0236e672009-09-02 21:12:08 -04001716 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1717 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001718 String text = nativeImageURI(contentX, contentY);
1719 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001720 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
1721 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001722 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
1723 result.setExtra(text);
1724 }
1725 }
1726 return result;
1727 }
1728
1729 /**
1730 * Request the href of an anchor element due to getFocusNodePath returning
1731 * "href." If hrefMsg is null, this method returns immediately and does not
1732 * dispatch hrefMsg to its target.
Cary Clarkd6982c92009-05-29 11:02:22 -04001733 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001734 * @param hrefMsg This message will be dispatched with the result of the
1735 * request as the data member with "url" as key. The result can
1736 * be null.
1737 */
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001738 // FIXME: API change required to change the name of this function. We now
1739 // look at the cursor node, and not the focus node. Also, what is
1740 // getFocusNodePath?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001741 public void requestFocusNodeHref(Message hrefMsg) {
1742 if (hrefMsg == null || mNativeClass == 0) {
1743 return;
1744 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001745 if (nativeCursorIsAnchor()) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001746 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
Cary Clarkd6982c92009-05-29 11:02:22 -04001747 nativeCursorFramePointer(), nativeCursorNodePointer(),
1748 hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001749 }
1750 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001751
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001752 /**
1753 * Request the url of the image last touched by the user. msg will be sent
1754 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04001755 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001756 * @param msg This message will be dispatched with the result of the request
1757 * as the data member with "url" as key. The result can be null.
1758 */
1759 public void requestImageRef(Message msg) {
Cary Clark7f970112009-10-15 15:29:08 -04001760 if (0 == mNativeClass) return; // client isn't initialized
Leon Scroggins0236e672009-09-02 21:12:08 -04001761 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1762 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001763 String ref = nativeImageURI(contentX, contentY);
1764 Bundle data = msg.getData();
1765 data.putString("url", ref);
1766 msg.setData(data);
1767 msg.sendToTarget();
1768 }
1769
1770 private static int pinLoc(int x, int viewMax, int docMax) {
1771// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
1772 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001773 // pin the short document to the top/left of the screen
1774 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001775// Log.d(LOGTAG, "--- center " + x);
1776 } else if (x < 0) {
1777 x = 0;
1778// Log.d(LOGTAG, "--- zero");
1779 } else if (x + viewMax > docMax) {
1780 x = docMax - viewMax;
1781// Log.d(LOGTAG, "--- pin " + x);
1782 }
1783 return x;
1784 }
1785
1786 // Expects x in view coordinates
1787 private int pinLocX(int x) {
1788 return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
1789 }
1790
1791 // Expects y in view coordinates
1792 private int pinLocY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04001793 int titleH = getTitleHeight();
1794 // if the titlebar is still visible, just pin against 0
1795 if (y <= titleH) {
1796 return Math.max(y, 0);
1797 }
1798 // convert to 0-based coordinate (subtract the title height)
1799 // pin(), and then add the title height back in
1800 return pinLoc(y - titleH, getViewHeight(),
1801 computeVerticalScrollRange()) + titleH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001802 }
1803
Leon Scroggins0236e672009-09-02 21:12:08 -04001804 /**
1805 * A title bar which is embedded in this WebView, and scrolls along with it
1806 * vertically, but not horizontally.
1807 */
1808 private View mTitleBar;
1809
1810 /**
Leon Scroggins58992ea2009-09-17 15:55:31 -04001811 * Since we draw the title bar ourselves, we removed the shadow from the
1812 * browser's activity. We do want a shadow at the bottom of the title bar,
1813 * or at the top of the screen if the title bar is not visible. This
1814 * drawable serves that purpose.
1815 */
1816 private Drawable mTitleShadow;
1817
1818 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001819 * Add or remove a title bar to be embedded into the WebView, and scroll
1820 * along with it vertically, while remaining in view horizontally. Pass
1821 * null to remove the title bar from the WebView, and return to drawing
1822 * the WebView normally without translating to account for the title bar.
1823 * @hide
1824 */
Leon Scroggins078c52c2009-09-04 16:58:09 -04001825 public void setEmbeddedTitleBar(View v) {
1826 if (mTitleBar == v) return;
1827 if (mTitleBar != null) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001828 removeView(mTitleBar);
Leon Scroggins078c52c2009-09-04 16:58:09 -04001829 }
1830 if (null != v) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001831 addView(v, new AbsoluteLayout.LayoutParams(
1832 ViewGroup.LayoutParams.FILL_PARENT,
Leon Scroggins078c52c2009-09-04 16:58:09 -04001833 ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
Leon Scroggins58992ea2009-09-17 15:55:31 -04001834 if (mTitleShadow == null) {
1835 mTitleShadow = (Drawable) mContext.getResources().getDrawable(
1836 com.android.internal.R.drawable.title_bar_shadow);
1837 }
Leon Scroggins0236e672009-09-02 21:12:08 -04001838 }
1839 mTitleBar = v;
1840 }
1841
1842 /**
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001843 * Given a distance in view space, convert it to content space. Note: this
1844 * does not reflect translation, just scaling, so this should not be called
1845 * with coordinates, but should be called for dimensions like width or
1846 * height.
1847 */
1848 private int viewToContentDimension(int d) {
1849 return Math.round(d * mInvActualScale);
1850 }
1851
1852 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001853 * Given an x coordinate in view space, convert it to content space. Also
1854 * may be used for absolute heights (such as for the WebTextView's
1855 * textSize, which is unaffected by the height of the title bar).
1856 */
1857 /*package*/ int viewToContentX(int x) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001858 return viewToContentDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001859 }
1860
Leon Scroggins0236e672009-09-02 21:12:08 -04001861 /**
1862 * Given a y coordinate in view space, convert it to content space.
1863 * Takes into account the height of the title bar if there is one
1864 * embedded into the WebView.
1865 */
1866 /*package*/ int viewToContentY(int y) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001867 return viewToContentDimension(y - getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04001868 }
1869
1870 /**
1871 * Given a distance in content space, convert it to view space. Note: this
1872 * does not reflect translation, just scaling, so this should not be called
1873 * with coordinates, but should be called for dimensions like width or
1874 * height.
1875 */
1876 /*package*/ int contentToViewDimension(int d) {
1877 return Math.round(d * mActualScale);
Leon Scroggins0236e672009-09-02 21:12:08 -04001878 }
1879
1880 /**
1881 * Given an x coordinate in content space, convert it to view
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001882 * space.
Leon Scroggins0236e672009-09-02 21:12:08 -04001883 */
1884 /*package*/ int contentToViewX(int x) {
Mike Reede8853fc2009-09-04 14:01:48 -04001885 return contentToViewDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886 }
1887
Leon Scroggins0236e672009-09-02 21:12:08 -04001888 /**
1889 * Given a y coordinate in content space, convert it to view
1890 * space. Takes into account the height of the title bar.
1891 */
1892 /*package*/ int contentToViewY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04001893 return contentToViewDimension(y) + getTitleHeight();
Leon Scroggins0236e672009-09-02 21:12:08 -04001894 }
1895
Mike Reede9e86b82009-09-15 11:26:53 -04001896 private Rect contentToViewRect(Rect x) {
1897 return new Rect(contentToViewX(x.left), contentToViewY(x.top),
1898 contentToViewX(x.right), contentToViewY(x.bottom));
1899 }
1900
1901 /* To invalidate a rectangle in content coordinates, we need to transform
1902 the rect into view coordinates, so we can then call invalidate(...).
1903
1904 Normally, we would just call contentToView[XY](...), which eventually
1905 calls Math.round(coordinate * mActualScale). However, for invalidates,
1906 we need to account for the slop that occurs with antialiasing. To
1907 address that, we are a little more liberal in the size of the rect that
1908 we invalidate.
1909
1910 This liberal calculation calls floor() for the top/left, and ceil() for
1911 the bottom/right coordinates. This catches the possible extra pixels of
1912 antialiasing that we might have missed with just round().
1913 */
1914
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001915 // Called by JNI to invalidate the View, given rectangle coordinates in
1916 // content space
1917 private void viewInvalidate(int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04001918 final float scale = mActualScale;
1919 final int dy = getTitleHeight();
1920 invalidate((int)Math.floor(l * scale),
1921 (int)Math.floor(t * scale) + dy,
1922 (int)Math.ceil(r * scale),
1923 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001924 }
1925
1926 // Called by JNI to invalidate the View after a delay, given rectangle
1927 // coordinates in content space
1928 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04001929 final float scale = mActualScale;
1930 final int dy = getTitleHeight();
1931 postInvalidateDelayed(delay,
1932 (int)Math.floor(l * scale),
1933 (int)Math.floor(t * scale) + dy,
1934 (int)Math.ceil(r * scale),
1935 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001936 }
1937
Mike Reede9e86b82009-09-15 11:26:53 -04001938 private void invalidateContentRect(Rect r) {
1939 viewInvalidate(r.left, r.top, r.right, r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001940 }
1941
Cary Clark278ce052009-08-31 16:08:42 -04001942 // stop the scroll animation, and don't let a subsequent fling add
1943 // to the existing velocity
1944 private void abortAnimation() {
1945 mScroller.abortAnimation();
1946 mLastVelocity = 0;
1947 }
1948
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001949 /* call from webcoreview.draw(), so we're still executing in the UI thread
1950 */
1951 private void recordNewContentSize(int w, int h, boolean updateLayout) {
1952
1953 // premature data from webkit, ignore
1954 if ((w | h) == 0) {
1955 return;
1956 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001957
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001958 // don't abort a scroll animation if we didn't change anything
1959 if (mContentWidth != w || mContentHeight != h) {
1960 // record new dimensions
1961 mContentWidth = w;
1962 mContentHeight = h;
1963 // If history Picture is drawn, don't update scroll. They will be
1964 // updated when we get out of that mode.
1965 if (!mDrawHistory) {
1966 // repin our scroll, taking into account the new content size
1967 int oldX = mScrollX;
1968 int oldY = mScrollY;
1969 mScrollX = pinLocX(mScrollX);
1970 mScrollY = pinLocY(mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001971 if (oldX != mScrollX || oldY != mScrollY) {
1972 sendOurVisibleRect();
1973 }
Leon Scrogginsd84e7d52009-09-29 11:11:45 -04001974 if (!mScroller.isFinished()) {
1975 // We are in the middle of a scroll. Repin the final scroll
1976 // position.
1977 mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
1978 mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
1979 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001980 }
1981 }
1982 contentSizeChanged(updateLayout);
1983 }
1984
1985 private void setNewZoomScale(float scale, boolean force) {
1986 if (scale < mMinZoomScale) {
1987 scale = mMinZoomScale;
1988 } else if (scale > mMaxZoomScale) {
1989 scale = mMaxZoomScale;
1990 }
1991 if (scale != mActualScale || force) {
1992 if (mDrawHistory) {
1993 // If history Picture is drawn, don't update scroll. They will
1994 // be updated when we get out of that mode.
1995 if (scale != mActualScale && !mPreviewZoomOnly) {
1996 mCallbackProxy.onScaleChanged(mActualScale, scale);
1997 }
1998 mActualScale = scale;
1999 mInvActualScale = 1 / scale;
2000 if (!mPreviewZoomOnly) {
2001 sendViewSizeZoom();
2002 }
2003 } else {
2004 // update our scroll so we don't appear to jump
2005 // i.e. keep the center of the doc in the center of the view
2006
2007 int oldX = mScrollX;
2008 int oldY = mScrollY;
2009 float ratio = scale * mInvActualScale; // old inverse
2010 float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
Grace Klobac0c03af2009-09-17 11:01:44 -07002011 float sy = ratio * oldY + (ratio - 1)
2012 * (mZoomCenterY - getTitleHeight());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002013
2014 // now update our new scale and inverse
2015 if (scale != mActualScale && !mPreviewZoomOnly) {
2016 mCallbackProxy.onScaleChanged(mActualScale, scale);
2017 }
2018 mActualScale = scale;
2019 mInvActualScale = 1 / scale;
2020
Patrick Scott0a5ce012009-07-02 08:56:10 -04002021 // Scale all the child views
2022 mViewManager.scaleAll();
2023
Cary Clarkd6982c92009-05-29 11:02:22 -04002024 // as we don't have animation for scaling, don't do animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002025 // for scrolling, as it causes weird intermediate state
2026 // pinScrollTo(Math.round(sx), Math.round(sy));
2027 mScrollX = pinLocX(Math.round(sx));
2028 mScrollY = pinLocY(Math.round(sy));
2029
2030 if (!mPreviewZoomOnly) {
2031 sendViewSizeZoom();
2032 sendOurVisibleRect();
2033 }
2034 }
2035 }
2036 }
2037
2038 // Used to avoid sending many visible rect messages.
2039 private Rect mLastVisibleRectSent;
2040 private Rect mLastGlobalRect;
2041
2042 private Rect sendOurVisibleRect() {
2043 Rect rect = new Rect();
2044 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002045 // Rect.equals() checks for null input.
2046 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04002047 Point pos = new Point(rect.left, rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002048 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Cary Clarked56eda2009-06-18 09:48:47 -04002049 nativeMoveGeneration(), 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002050 mLastVisibleRectSent = rect;
2051 }
2052 Rect globalRect = new Rect();
2053 if (getGlobalVisibleRect(globalRect)
2054 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04002055 if (DebugFlags.WEB_VIEW) {
2056 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
2057 + globalRect.top + ",r=" + globalRect.right + ",b="
2058 + globalRect.bottom);
2059 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002060 // TODO: the global offset is only used by windowRect()
2061 // in ChromeClientAndroid ; other clients such as touch
2062 // and mouse events could return view + screen relative points.
2063 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
2064 mLastGlobalRect = globalRect;
2065 }
2066 return rect;
2067 }
2068
2069 // Sets r to be the visible rectangle of our webview in view coordinates
2070 private void calcOurVisibleRect(Rect r) {
2071 Point p = new Point();
2072 getGlobalVisibleRect(r, p);
2073 r.offset(-p.x, -p.y);
Cary Clark3524be92009-06-22 13:09:11 -04002074 if (mFindIsUp) {
Cary Clark5bb6b522009-09-21 11:58:31 -04002075 r.bottom -= mFindHeight;
Cary Clark3524be92009-06-22 13:09:11 -04002076 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002077 }
2078
2079 // Sets r to be our visible rectangle in content coordinates
2080 private void calcOurContentVisibleRect(Rect r) {
2081 calcOurVisibleRect(r);
Leon Scroggins0236e672009-09-02 21:12:08 -04002082 r.left = viewToContentX(r.left);
Leon Scroggins37df6a82009-09-23 10:31:23 -04002083 // viewToContentY will remove the total height of the title bar. Add
2084 // the visible height back in to account for the fact that if the title
2085 // bar is partially visible, the part of the visible rect which is
2086 // displaying our content is displaced by that amount.
Grace Kloba8eff73f2009-09-24 09:34:32 -07002087 r.top = viewToContentY(r.top + getVisibleTitleHeight());
Leon Scroggins0236e672009-09-02 21:12:08 -04002088 r.right = viewToContentX(r.right);
Grace Kloba8eff73f2009-09-24 09:34:32 -07002089 r.bottom = viewToContentY(r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002090 }
2091
Grace Klobaef347ef2009-07-30 11:20:32 -07002092 static class ViewSizeData {
2093 int mWidth;
2094 int mHeight;
2095 int mTextWrapWidth;
2096 float mScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002097 boolean mIgnoreHeight;
Grace Klobaef347ef2009-07-30 11:20:32 -07002098 }
2099
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002100 /**
2101 * Compute unzoomed width and height, and if they differ from the last
2102 * values we sent, send them to webkit (to be used has new viewport)
2103 *
2104 * @return true if new values were sent
2105 */
2106 private boolean sendViewSizeZoom() {
Grace Klobaef347ef2009-07-30 11:20:32 -07002107 int viewWidth = getViewWidth();
2108 int newWidth = Math.round(viewWidth * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002109 int newHeight = Math.round(getViewHeight() * mInvActualScale);
2110 /*
2111 * Because the native side may have already done a layout before the
2112 * View system was able to measure us, we have to send a height of 0 to
2113 * remove excess whitespace when we grow our width. This will trigger a
2114 * layout and a change in content size. This content size change will
2115 * mean that contentSizeChanged will either call this method directly or
2116 * indirectly from onSizeChanged.
2117 */
2118 if (newWidth > mLastWidthSent && mWrapContent) {
2119 newHeight = 0;
2120 }
2121 // Avoid sending another message if the dimensions have not changed.
2122 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
Grace Klobaef347ef2009-07-30 11:20:32 -07002123 ViewSizeData data = new ViewSizeData();
2124 data.mWidth = newWidth;
2125 data.mHeight = newHeight;
2126 // while in zoom overview mode, the text are wrapped to the screen
2127 // width matching mLastScale. So that we don't trigger re-flow while
2128 // toggling between overview mode and normal mode.
2129 data.mTextWrapWidth = mInZoomOverview ? Math.round(viewWidth
2130 / mLastScale) : newWidth;
2131 data.mScale = mActualScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002132 data.mIgnoreHeight = mZoomScale != 0 && !mHeightCanMeasure;
Grace Klobaef347ef2009-07-30 11:20:32 -07002133 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002134 mLastWidthSent = newWidth;
2135 mLastHeightSent = newHeight;
2136 return true;
2137 }
2138 return false;
2139 }
2140
2141 @Override
2142 protected int computeHorizontalScrollRange() {
2143 if (mDrawHistory) {
2144 return mHistoryWidth;
2145 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002146 // to avoid rounding error caused unnecessary scrollbar, use floor
2147 return (int) Math.floor(mContentWidth * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002148 }
2149 }
2150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002151 @Override
2152 protected int computeVerticalScrollRange() {
2153 if (mDrawHistory) {
2154 return mHistoryHeight;
2155 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002156 // to avoid rounding error caused unnecessary scrollbar, use floor
2157 return (int) Math.floor(mContentHeight * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002158 }
2159 }
2160
Leon Scroggins0236e672009-09-02 21:12:08 -04002161 @Override
2162 protected int computeVerticalScrollOffset() {
Mike Reede8853fc2009-09-04 14:01:48 -04002163 return Math.max(mScrollY - getTitleHeight(), 0);
Leon Scroggins0236e672009-09-02 21:12:08 -04002164 }
2165
2166 @Override
2167 protected int computeVerticalScrollExtent() {
2168 return getViewHeight();
2169 }
2170
Mike Reede8853fc2009-09-04 14:01:48 -04002171 /** @hide */
2172 @Override
2173 protected void onDrawVerticalScrollBar(Canvas canvas,
2174 Drawable scrollBar,
2175 int l, int t, int r, int b) {
2176 scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
2177 scrollBar.draw(canvas);
2178 }
2179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002180 /**
2181 * Get the url for the current page. This is not always the same as the url
2182 * passed to WebViewClient.onPageStarted because although the load for
2183 * that url has begun, the current page may not have changed.
2184 * @return The url for the current page.
2185 */
2186 public String getUrl() {
2187 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2188 return h != null ? h.getUrl() : null;
2189 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002190
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002191 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002192 * Get the original url for the current page. This is not always the same
2193 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002194 * load for that url has begun, the current page may not have changed.
2195 * Also, there may have been redirects resulting in a different url to that
2196 * originally requested.
2197 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002198 */
2199 public String getOriginalUrl() {
2200 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2201 return h != null ? h.getOriginalUrl() : null;
2202 }
2203
2204 /**
2205 * Get the title for the current page. This is the title of the current page
2206 * until WebViewClient.onReceivedTitle is called.
2207 * @return The title for the current page.
2208 */
2209 public String getTitle() {
2210 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2211 return h != null ? h.getTitle() : null;
2212 }
2213
2214 /**
2215 * Get the favicon for the current page. This is the favicon of the current
2216 * page until WebViewClient.onReceivedIcon is called.
2217 * @return The favicon for the current page.
2218 */
2219 public Bitmap getFavicon() {
2220 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2221 return h != null ? h.getFavicon() : null;
2222 }
2223
2224 /**
Patrick Scott2ba12622009-08-04 13:20:05 -04002225 * Get the touch icon url for the apple-touch-icon <link> element.
2226 * @hide
2227 */
2228 public String getTouchIconUrl() {
2229 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2230 return h != null ? h.getTouchIconUrl() : null;
2231 }
2232
2233 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002234 * Get the progress for the current page.
2235 * @return The progress for the current page between 0 and 100.
2236 */
2237 public int getProgress() {
2238 return mCallbackProxy.getProgress();
2239 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002241 /**
2242 * @return the height of the HTML content.
2243 */
2244 public int getContentHeight() {
2245 return mContentHeight;
2246 }
2247
2248 /**
Leon Scrogginsea96d1e2009-09-23 13:41:01 -04002249 * @return the width of the HTML content.
2250 * @hide
2251 */
2252 public int getContentWidth() {
2253 return mContentWidth;
2254 }
2255
2256 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002257 * Pause all layout, parsing, and javascript timers for all webviews. This
2258 * is a global requests, not restricted to just this webview. This can be
2259 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002260 */
2261 public void pauseTimers() {
2262 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
2263 }
2264
2265 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002266 * Resume all layout, parsing, and javascript timers for all webviews.
2267 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002268 */
2269 public void resumeTimers() {
2270 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
2271 }
2272
2273 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002274 * Call this to pause any extra processing associated with this view and
2275 * its associated DOM/plugins/javascript/etc. For example, if the view is
2276 * taken offscreen, this could be called to reduce unnecessary CPU and/or
2277 * network traffic. When the view is again "active", call onResume().
2278 *
2279 * Note that this differs from pauseTimers(), which affects all views/DOMs
2280 * @hide
2281 */
2282 public void onPause() {
2283 if (!mIsPaused) {
2284 mIsPaused = true;
2285 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
2286 }
2287 }
2288
2289 /**
2290 * Call this to balanace a previous call to onPause()
2291 * @hide
2292 */
2293 public void onResume() {
2294 if (mIsPaused) {
2295 mIsPaused = false;
2296 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2297 }
2298 }
2299
2300 /**
2301 * Returns true if the view is paused, meaning onPause() was called. Calling
2302 * onResume() sets the paused state back to false.
2303 * @hide
2304 */
2305 public boolean isPaused() {
2306 return mIsPaused;
2307 }
2308
2309 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002310 * Call this to inform the view that memory is low so that it can
2311 * free any available memory.
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002312 */
2313 public void freeMemory() {
2314 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2315 }
2316
2317 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002318 * Clear the resource cache. Note that the cache is per-application, so
2319 * this will clear the cache for all WebViews used.
2320 *
2321 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002322 */
2323 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002324 // Note: this really needs to be a static method as it clears cache for all
2325 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2326 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002327 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2328 includeDiskFiles ? 1 : 0, 0);
2329 }
2330
2331 /**
2332 * Make sure that clearing the form data removes the adapter from the
2333 * currently focused textfield if there is one.
2334 */
2335 public void clearFormData() {
2336 if (inEditingMode()) {
2337 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002338 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002339 }
2340 }
2341
2342 /**
2343 * Tell the WebView to clear its internal back/forward list.
2344 */
2345 public void clearHistory() {
2346 mCallbackProxy.getBackForwardList().setClearPending();
2347 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2348 }
2349
2350 /**
2351 * Clear the SSL preferences table stored in response to proceeding with SSL
2352 * certificate errors.
2353 */
2354 public void clearSslPreferences() {
2355 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2356 }
2357
2358 /**
2359 * Return the WebBackForwardList for this WebView. This contains the
2360 * back/forward list for use in querying each item in the history stack.
2361 * This is a copy of the private WebBackForwardList so it contains only a
2362 * snapshot of the current state. Multiple calls to this method may return
2363 * different objects. The object returned from this method will not be
2364 * updated to reflect any new state.
2365 */
2366 public WebBackForwardList copyBackForwardList() {
2367 return mCallbackProxy.getBackForwardList().clone();
2368 }
2369
2370 /*
2371 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04002372 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002373 * calling findAll.
2374 *
2375 * @param forward Direction to search.
2376 */
2377 public void findNext(boolean forward) {
Cary Clark7f970112009-10-15 15:29:08 -04002378 if (0 == mNativeClass) return; // client isn't initialized
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002379 nativeFindNext(forward);
2380 }
2381
2382 /*
2383 * Find all instances of find on the page and highlight them.
2384 * @param find String to find.
2385 * @return int The number of occurances of the String "find"
2386 * that were found.
2387 */
2388 public int findAll(String find) {
Cary Clark7f970112009-10-15 15:29:08 -04002389 if (0 == mNativeClass) return 0; // client isn't initialized
Cary Clark5bb6b522009-09-21 11:58:31 -04002390 if (mFindIsUp == false) {
2391 recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
2392 false);
2393 mFindIsUp = true;
2394 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002395 int result = nativeFindAll(find.toLowerCase(), find.toUpperCase());
2396 invalidate();
Leon Scroggins5de63892009-10-29 09:48:43 -04002397 mLastFind = find;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002398 return result;
2399 }
2400
2401 // Used to know whether the find dialog is open. Affects whether
2402 // or not we draw the highlights for matches.
2403 private boolean mFindIsUp;
Cary Clark5bb6b522009-09-21 11:58:31 -04002404 private int mFindHeight;
Leon Scroggins5de63892009-10-29 09:48:43 -04002405 // Keep track of the last string sent, so we can search again after an
2406 // orientation change or the dismissal of the soft keyboard.
2407 private String mLastFind;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002408
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002409 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002410 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002411 * location. Currently, only addresses in the United States are detected,
2412 * and consist of:
2413 * - a house number
2414 * - a street name
2415 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2416 * - a city name
2417 * - a state or territory, either spelled out or two-letter abbr.
2418 * - an optional 5 digit or 9 digit zip code.
2419 *
2420 * All names must be correctly capitalized, and the zip code, if present,
2421 * must be valid for the state. The street type must be a standard USPS
2422 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04002423 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002424 * five digits.
2425 * @param addr The string to search for addresses.
2426 *
2427 * @return the address, or if no address is found, return null.
2428 */
2429 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04002430 return findAddress(addr, false);
2431 }
2432
2433 /**
2434 * @hide
2435 * Return the first substring consisting of the address of a physical
2436 * location. Currently, only addresses in the United States are detected,
2437 * and consist of:
2438 * - a house number
2439 * - a street name
2440 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2441 * - a city name
2442 * - a state or territory, either spelled out or two-letter abbr.
2443 * - an optional 5 digit or 9 digit zip code.
2444 *
2445 * Names are optionally capitalized, and the zip code, if present,
2446 * must be valid for the state. The street type must be a standard USPS
2447 * spelling or abbreviation. The state or territory must also be spelled
2448 * or abbreviated using USPS standards. The house number may not exceed
2449 * five digits.
2450 * @param addr The string to search for addresses.
2451 * @param caseInsensitive addr Set to true to make search ignore case.
2452 *
2453 * @return the address, or if no address is found, return null.
2454 */
2455 public static String findAddress(String addr, boolean caseInsensitive) {
2456 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002457 }
2458
2459 /*
2460 * Clear the highlighting surrounding text matches created by findAll.
2461 */
2462 public void clearMatches() {
Cary Clark32847a92009-11-17 16:04:18 -05002463 if (mNativeClass == 0)
2464 return;
Cary Clark5bb6b522009-09-21 11:58:31 -04002465 if (mFindIsUp) {
2466 recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
2467 false);
2468 mFindIsUp = false;
2469 }
Cary Clark32847a92009-11-17 16:04:18 -05002470 nativeSetFindIsUp();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002471 // Now that the dialog has been removed, ensure that we scroll to a
2472 // location that is not beyond the end of the page.
2473 pinScrollTo(mScrollX, mScrollY, false, 0);
2474 invalidate();
2475 }
2476
2477 /**
Cary Clark5bb6b522009-09-21 11:58:31 -04002478 * @hide
2479 */
2480 public void setFindDialogHeight(int height) {
2481 if (DebugFlags.WEB_VIEW) {
2482 Log.v(LOGTAG, "setFindDialogHeight height=" + height);
2483 }
2484 mFindHeight = height;
2485 }
2486
2487 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002488 * Query the document to see if it contains any image references. The
2489 * message object will be dispatched with arg1 being set to 1 if images
2490 * were found and 0 if the document does not reference any images.
2491 * @param response The message that will be dispatched with the result.
2492 */
2493 public void documentHasImages(Message response) {
2494 if (response == null) {
2495 return;
2496 }
2497 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
2498 }
2499
2500 @Override
2501 public void computeScroll() {
2502 if (mScroller.computeScrollOffset()) {
2503 int oldX = mScrollX;
2504 int oldY = mScrollY;
2505 mScrollX = mScroller.getCurrX();
2506 mScrollY = mScroller.getCurrY();
2507 postInvalidate(); // So we draw again
2508 if (oldX != mScrollX || oldY != mScrollY) {
2509 // as onScrollChanged() is not called, sendOurVisibleRect()
2510 // needs to be call explicitly
2511 sendOurVisibleRect();
2512 }
2513 } else {
2514 super.computeScroll();
2515 }
2516 }
2517
2518 private static int computeDuration(int dx, int dy) {
2519 int distance = Math.max(Math.abs(dx), Math.abs(dy));
2520 int duration = distance * 1000 / STD_SPEED;
2521 return Math.min(duration, MAX_DURATION);
2522 }
2523
2524 // helper to pin the scrollBy parameters (already in view coordinates)
2525 // returns true if the scroll was changed
2526 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
2527 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
2528 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002529 // helper to pin the scrollTo parameters (already in view coordinates)
2530 // returns true if the scroll was changed
2531 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
2532 x = pinLocX(x);
2533 y = pinLocY(y);
2534 int dx = x - mScrollX;
2535 int dy = y - mScrollY;
2536
2537 if ((dx | dy) == 0) {
2538 return false;
2539 }
Leon Scrogginsd55de402009-09-17 14:19:49 -04002540 if (animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002541 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002542 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
2543 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
Mike Cleronf116bf82009-09-27 19:14:12 -07002544 awakenScrollBars(mScroller.getDuration());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002545 invalidate();
2546 } else {
Cary Clark278ce052009-08-31 16:08:42 -04002547 abortAnimation(); // just in case
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002548 scrollTo(x, y);
2549 }
2550 return true;
2551 }
2552
2553 // Scale from content to view coordinates, and pin.
2554 // Also called by jni webview.cpp
Leon Scroggins4c943042009-07-31 15:05:42 -04002555 private boolean setContentScrollBy(int cx, int cy, boolean animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002556 if (mDrawHistory) {
2557 // disallow WebView to change the scroll position as History Picture
2558 // is used in the view system.
2559 // TODO: as we switchOutDrawHistory when trackball or navigation
2560 // keys are hit, this should be safe. Right?
Leon Scroggins4c943042009-07-31 15:05:42 -04002561 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002562 }
Mike Reede8853fc2009-09-04 14:01:48 -04002563 cx = contentToViewDimension(cx);
2564 cy = contentToViewDimension(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002565 if (mHeightCanMeasure) {
2566 // move our visible rect according to scroll request
2567 if (cy != 0) {
2568 Rect tempRect = new Rect();
2569 calcOurVisibleRect(tempRect);
2570 tempRect.offset(cx, cy);
2571 requestRectangleOnScreen(tempRect);
2572 }
2573 // FIXME: We scroll horizontally no matter what because currently
2574 // ScrollView and ListView will not scroll horizontally.
2575 // FIXME: Why do we only scroll horizontally if there is no
2576 // vertical scroll?
2577// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
Leon Scroggins4c943042009-07-31 15:05:42 -04002578 return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002579 } else {
Leon Scroggins4c943042009-07-31 15:05:42 -04002580 return pinScrollBy(cx, cy, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002581 }
2582 }
2583
Leon Scroggins405d7852009-11-02 14:50:54 -08002584 /**
2585 * Called by CallbackProxy when the page finishes loading.
2586 * @param url The URL of the page which has finished loading.
2587 */
2588 /* package */ void onPageFinished(String url) {
2589 if (mPageThatNeedsToSlideTitleBarOffScreen != null) {
2590 // If the user is now on a different page, or has scrolled the page
2591 // past the point where the title bar is offscreen, ignore the
2592 // scroll request.
2593 if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url)
2594 && mScrollX == 0 && mScrollY == 0) {
2595 pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true,
2596 SLIDE_TITLE_DURATION);
2597 }
2598 mPageThatNeedsToSlideTitleBarOffScreen = null;
2599 }
2600 }
2601
2602 /**
2603 * The URL of a page that sent a message to scroll the title bar off screen.
2604 *
2605 * Many mobile sites tell the page to scroll to (0,1) in order to scroll the
2606 * title bar off the screen. Sometimes, the scroll position is set before
2607 * the page finishes loading. Rather than scrolling while the page is still
2608 * loading, keep track of the URL and new scroll position so we can perform
2609 * the scroll once the page finishes loading.
2610 */
2611 private String mPageThatNeedsToSlideTitleBarOffScreen;
2612
2613 /**
2614 * The destination Y scroll position to be used when the page finishes
2615 * loading. See mPageThatNeedsToSlideTitleBarOffScreen.
2616 */
2617 private int mYDistanceToSlideTitleOffScreen;
2618
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002619 // scale from content to view coordinates, and pin
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002620 // return true if pin caused the final x/y different than the request cx/cy,
2621 // and a future scroll may reach the request cx/cy after our size has
2622 // changed
2623 // return false if the view scroll to the exact position as it is requested,
2624 // where negative numbers are taken to mean 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002625 private boolean setContentScrollTo(int cx, int cy) {
2626 if (mDrawHistory) {
2627 // disallow WebView to change the scroll position as History Picture
2628 // is used in the view system.
2629 // One known case where this is called is that WebCore tries to
2630 // restore the scroll position. As history Picture already uses the
2631 // saved scroll position, it is ok to skip this.
2632 return false;
2633 }
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002634 int vx;
2635 int vy;
2636 if ((cx | cy) == 0) {
2637 // If the page is being scrolled to (0,0), do not add in the title
2638 // bar's height, and simply scroll to (0,0). (The only other work
2639 // in contentToView_ is to multiply, so this would not change 0.)
2640 vx = 0;
2641 vy = 0;
2642 } else {
2643 vx = contentToViewX(cx);
2644 vy = contentToViewY(cy);
2645 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002646// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
2647// vx + " " + vy + "]");
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002648 // Some mobile sites attempt to scroll the title bar off the page by
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002649 // scrolling to (0,1). If we are at the top left corner of the
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002650 // page, assume this is an attempt to scroll off the title bar, and
2651 // animate the title bar off screen slowly enough that the user can see
2652 // it.
Leon Scroggins405d7852009-11-02 14:50:54 -08002653 if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0
2654 && mTitleBar != null) {
2655 // FIXME: 100 should be defined somewhere as our max progress.
2656 if (getProgress() < 100) {
2657 // Wait to scroll the title bar off screen until the page has
2658 // finished loading. Keep track of the URL and the destination
2659 // Y position
2660 mPageThatNeedsToSlideTitleBarOffScreen = getUrl();
2661 mYDistanceToSlideTitleOffScreen = vy;
2662 } else {
2663 pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
2664 }
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002665 // Since we are animating, we have not yet reached the desired
2666 // scroll position. Do not return true to request another attempt
2667 return false;
2668 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002669 pinScrollTo(vx, vy, false, 0);
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002670 // If the request was to scroll to a negative coordinate, treat it as if
2671 // it was a request to scroll to 0
2672 if ((mScrollX != vx && cx >= 0) || (mScrollY != vy && cy >= 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002673 return true;
2674 } else {
2675 return false;
2676 }
2677 }
2678
2679 // scale from content to view coordinates, and pin
2680 private void spawnContentScrollTo(int cx, int cy) {
2681 if (mDrawHistory) {
2682 // disallow WebView to change the scroll position as History Picture
2683 // is used in the view system.
2684 return;
2685 }
Leon Scroggins0236e672009-09-02 21:12:08 -04002686 int vx = contentToViewX(cx);
2687 int vy = contentToViewY(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002688 pinScrollTo(vx, vy, true, 0);
2689 }
2690
2691 /**
2692 * These are from webkit, and are in content coordinate system (unzoomed)
2693 */
2694 private void contentSizeChanged(boolean updateLayout) {
2695 // suppress 0,0 since we usually see real dimensions soon after
2696 // this avoids drawing the prev content in a funny place. If we find a
2697 // way to consolidate these notifications, this check may become
2698 // obsolete
2699 if ((mContentWidth | mContentHeight) == 0) {
2700 return;
2701 }
2702
2703 if (mHeightCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002704 if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002705 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002706 requestLayout();
2707 }
2708 } else if (mWidthCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002709 if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002710 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002711 requestLayout();
2712 }
2713 } else {
2714 // If we don't request a layout, try to send our view size to the
2715 // native side to ensure that WebCore has the correct dimensions.
2716 sendViewSizeZoom();
2717 }
2718 }
2719
2720 /**
2721 * Set the WebViewClient that will receive various notifications and
2722 * requests. This will replace the current handler.
2723 * @param client An implementation of WebViewClient.
2724 */
2725 public void setWebViewClient(WebViewClient client) {
2726 mCallbackProxy.setWebViewClient(client);
2727 }
2728
2729 /**
Grace Kloba94ab3b62009-10-07 18:00:19 -07002730 * Gets the WebViewClient
2731 * @return the current WebViewClient instance.
2732 *
2733 *@hide pending API council approval.
2734 */
2735 public WebViewClient getWebViewClient() {
2736 return mCallbackProxy.getWebViewClient();
2737 }
2738
2739 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002740 * Register the interface to be used when content can not be handled by
2741 * the rendering engine, and should be downloaded instead. This will replace
2742 * the current handler.
2743 * @param listener An implementation of DownloadListener.
2744 */
2745 public void setDownloadListener(DownloadListener listener) {
2746 mCallbackProxy.setDownloadListener(listener);
2747 }
2748
2749 /**
2750 * Set the chrome handler. This is an implementation of WebChromeClient for
2751 * use in handling Javascript dialogs, favicons, titles, and the progress.
2752 * This will replace the current handler.
2753 * @param client An implementation of WebChromeClient.
2754 */
2755 public void setWebChromeClient(WebChromeClient client) {
2756 mCallbackProxy.setWebChromeClient(client);
2757 }
2758
2759 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01002760 * Gets the chrome handler.
2761 * @return the current WebChromeClient instance.
2762 *
2763 * @hide API council approval.
2764 */
2765 public WebChromeClient getWebChromeClient() {
2766 return mCallbackProxy.getWebChromeClient();
2767 }
2768
2769 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002770 * Set the Picture listener. This is an interface used to receive
2771 * notifications of a new Picture.
2772 * @param listener An implementation of WebView.PictureListener.
2773 */
2774 public void setPictureListener(PictureListener listener) {
2775 mPictureListener = listener;
2776 }
2777
2778 /**
2779 * {@hide}
2780 */
2781 /* FIXME: Debug only! Remove for SDK! */
2782 public void externalRepresentation(Message callback) {
2783 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
2784 }
2785
2786 /**
2787 * {@hide}
2788 */
2789 /* FIXME: Debug only! Remove for SDK! */
2790 public void documentAsText(Message callback) {
2791 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
2792 }
2793
2794 /**
2795 * Use this function to bind an object to Javascript so that the
2796 * methods can be accessed from Javascript.
2797 * <p><strong>IMPORTANT:</strong>
2798 * <ul>
2799 * <li> Using addJavascriptInterface() allows JavaScript to control your
2800 * application. This can be a very useful feature or a dangerous security
2801 * issue. When the HTML in the WebView is untrustworthy (for example, part
2802 * or all of the HTML is provided by some person or process), then an
2803 * attacker could inject HTML that will execute your code and possibly any
2804 * code of the attacker's choosing.<br>
2805 * Do not use addJavascriptInterface() unless all of the HTML in this
2806 * WebView was written by you.</li>
2807 * <li> The Java object that is bound runs in another thread and not in
2808 * the thread that it was constructed in.</li>
2809 * </ul></p>
2810 * @param obj The class instance to bind to Javascript
2811 * @param interfaceName The name to used to expose the class in Javascript
2812 */
2813 public void addJavascriptInterface(Object obj, String interfaceName) {
Cary Clarkded054c2009-06-15 10:26:08 -04002814 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
2815 arg.mObject = obj;
2816 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002817 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
2818 }
2819
2820 /**
2821 * Return the WebSettings object used to control the settings for this
2822 * WebView.
2823 * @return A WebSettings object that can be used to control this WebView's
2824 * settings.
2825 */
2826 public WebSettings getSettings() {
2827 return mWebViewCore.getSettings();
2828 }
2829
2830 /**
2831 * Return the list of currently loaded plugins.
2832 * @return The list of currently loaded plugins.
Andrei Popescu385df692009-08-13 11:59:57 +01002833 *
2834 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002835 */
Andrei Popescu385df692009-08-13 11:59:57 +01002836 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002837 public static synchronized PluginList getPluginList() {
Grace Klobabb245ea2009-11-10 13:13:24 -08002838 return new PluginList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002839 }
2840
2841 /**
Andrei Popescu385df692009-08-13 11:59:57 +01002842 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002843 */
Andrei Popescu385df692009-08-13 11:59:57 +01002844 @Deprecated
2845 public void refreshPlugins(boolean reloadOpenPages) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002846
2847 //-------------------------------------------------------------------------
2848 // Override View methods
2849 //-------------------------------------------------------------------------
2850
2851 @Override
2852 protected void finalize() throws Throwable {
Cary Clark9a4c0632009-08-10 16:40:08 -04002853 try {
2854 destroy();
2855 } finally {
2856 super.finalize();
2857 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002858 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002859
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002860 @Override
Leon Scroggins0236e672009-09-02 21:12:08 -04002861 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
2862 if (child == mTitleBar) {
2863 // When drawing the title bar, move it horizontally to always show
2864 // at the top of the WebView.
2865 mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
2866 }
2867 return super.drawChild(canvas, child, drawingTime);
2868 }
2869
2870 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002871 protected void onDraw(Canvas canvas) {
2872 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
2873 if (mNativeClass == 0) {
2874 return;
2875 }
Leon Scroggins58992ea2009-09-17 15:55:31 -04002876 int saveCount = canvas.save();
2877 if (mTitleBar != null) {
2878 canvas.translate(0, (int) mTitleBar.getHeight());
2879 }
Grace Kloba04b28682009-09-14 14:38:37 -07002880 // Update the buttons in the picture, so when we draw the picture
2881 // to the screen, they are in the correct state.
2882 // Tell the native side if user is a) touching the screen,
2883 // b) pressing the trackball down, or c) pressing the enter key
2884 // If the cursor is on a button, we need to draw it in the pressed
2885 // state.
2886 // If mNativeClass is 0, we should not reach here, so we do not
2887 // need to check it again.
2888 nativeRecordButtons(hasFocus() && hasWindowFocus(),
2889 mTouchMode == TOUCH_SHORTPRESS_START_MODE
2890 || mTrackballDown || mGotCenterDown, false);
2891 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
Leon Scroggins0236e672009-09-02 21:12:08 -04002892 canvas.restoreToCount(saveCount);
Cary Clarkd6982c92009-05-29 11:02:22 -04002893
Leon Scroggins58992ea2009-09-17 15:55:31 -04002894 // Now draw the shadow.
2895 if (mTitleBar != null) {
2896 int y = mScrollY + getVisibleTitleHeight();
2897 int height = (int) (5f * getContext().getResources()
2898 .getDisplayMetrics().density);
2899 mTitleShadow.setBounds(mScrollX, y, mScrollX + getWidth(),
2900 y + height);
2901 mTitleShadow.draw(canvas);
2902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002903 if (AUTO_REDRAW_HACK && mAutoRedraw) {
2904 invalidate();
2905 }
2906 }
2907
2908 @Override
2909 public void setLayoutParams(ViewGroup.LayoutParams params) {
2910 if (params.height == LayoutParams.WRAP_CONTENT) {
2911 mWrapContent = true;
2912 }
2913 super.setLayoutParams(params);
2914 }
2915
2916 @Override
2917 public boolean performLongClick() {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04002918 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
2919 // Send the click so that the textfield is in focus
Leon Scroggins1d96ca02009-10-23 11:49:03 -04002920 centerKeyPressOnTextField();
Leon Scroggins3ccd3652009-06-26 17:22:50 -04002921 rebuildWebTextView();
2922 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002923 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002924 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002925 } else {
2926 return super.performLongClick();
2927 }
2928 }
2929
Grace Kloba94c715d2009-09-28 23:23:53 -07002930 boolean inAnimateZoom() {
2931 return mZoomScale != 0;
2932 }
2933
Leon Scroggins608f9f42009-09-03 10:06:04 -04002934 /**
2935 * Need to adjust the WebTextView after a change in zoom, since mActualScale
2936 * has changed. This is especially important for password fields, which are
2937 * drawn by the WebTextView, since it conveys more information than what
2938 * webkit draws. Thus we need to reposition it to show in the correct
2939 * place.
2940 */
2941 private boolean mNeedToAdjustWebTextView;
2942
Cary Clark5da9aeb2009-10-06 17:40:53 -04002943 private boolean didUpdateTextViewBounds(boolean allowIntersect) {
2944 Rect contentBounds = nativeFocusCandidateNodeBounds();
2945 Rect vBox = contentToViewRect(contentBounds);
2946 Rect visibleRect = new Rect();
2947 calcOurVisibleRect(visibleRect);
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05002948 // The IME may have shown, resulting in the textfield being offscreen.
2949 // If so, the textfield will be scrolled on screen, so treat it as
2950 // though it is on screen. If it is on screen, place the WebTextView in
2951 // its new place, accounting for our new scroll/zoom values.
2952 InputMethodManager imm = InputMethodManager.peekInstance();
2953 if ((imm != null && imm.isActive(mWebTextView))
2954 || (allowIntersect ? Rect.intersects(visibleRect, vBox)
2955 : visibleRect.contains(vBox))) {
Cary Clark5da9aeb2009-10-06 17:40:53 -04002956 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
2957 vBox.height());
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05002958 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
2959 contentToViewDimension(
2960 nativeFocusCandidateTextSize()));
Cary Clark5da9aeb2009-10-06 17:40:53 -04002961 return true;
2962 } else {
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05002963 // The textfield is now off screen. The user probably
2964 // was not zooming to see the textfield better. Remove
2965 // the WebTextView. If the user types a key, and the
2966 // textfield is still in focus, we will reconstruct
2967 // the WebTextView and scroll it back on screen.
2968 mWebTextView.remove();
Cary Clark5da9aeb2009-10-06 17:40:53 -04002969 return false;
2970 }
2971 }
2972
Cary Clarkd6982c92009-05-29 11:02:22 -04002973 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002974 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002975 if (mDrawHistory) {
2976 canvas.scale(mActualScale, mActualScale);
2977 canvas.drawPicture(mHistoryPicture);
2978 return;
2979 }
2980
2981 boolean animateZoom = mZoomScale != 0;
Cary Clark25415e22009-10-12 13:41:28 -04002982 boolean animateScroll = (!mScroller.isFinished()
2983 || mVelocityTracker != null)
2984 && (mTouchMode != TOUCH_DRAG_MODE ||
2985 mHeldMotionless != MOTIONLESS_TRUE);
2986 if (mTouchMode == TOUCH_DRAG_MODE) {
2987 if (mHeldMotionless == MOTIONLESS_PENDING) {
2988 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
2989 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
2990 mHeldMotionless = MOTIONLESS_FALSE;
2991 }
2992 if (mHeldMotionless == MOTIONLESS_FALSE) {
2993 mPrivateHandler.sendMessageDelayed(mPrivateHandler
2994 .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
2995 mHeldMotionless = MOTIONLESS_PENDING;
2996 }
2997 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002998 if (animateZoom) {
2999 float zoomScale;
3000 int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
3001 if (interval < ZOOM_ANIMATION_LENGTH) {
3002 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
Cary Clarkd6982c92009-05-29 11:02:22 -04003003 zoomScale = 1.0f / (mInvInitialZoomScale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003004 + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
3005 invalidate();
3006 } else {
3007 zoomScale = mZoomScale;
3008 // set mZoomScale to be 0 as we have done animation
3009 mZoomScale = 0;
Grace Klobadfe095a2009-09-17 07:56:48 -07003010 // call invalidate() again to draw with the final filters
3011 invalidate();
Leon Scroggins608f9f42009-09-03 10:06:04 -04003012 if (mNeedToAdjustWebTextView) {
3013 mNeedToAdjustWebTextView = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003014 if (didUpdateTextViewBounds(false)
3015 && nativeFocusCandidateIsPassword()) {
Leon Scroggins10be7542009-09-30 18:01:38 -04003016 // If it is a password field, start drawing the
3017 // WebTextView once again.
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003018 mWebTextView.setInPassword(true);
Leon Scroggins608f9f42009-09-03 10:06:04 -04003019 }
3020 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003021 }
Grace Klobac0c03af2009-09-17 11:01:44 -07003022 // calculate the intermediate scroll position. As we need to use
3023 // zoomScale, we can't use pinLocX/Y directly. Copy the logic here.
Grace Kloba675c7d22009-07-23 09:21:21 -07003024 float scale = zoomScale * mInvInitialZoomScale;
3025 int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
3026 - mZoomCenterX);
3027 tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
3028 * zoomScale)) + mScrollX;
Grace Klobac0c03af2009-09-17 11:01:44 -07003029 int titleHeight = getTitleHeight();
3030 int ty = Math.round(scale
3031 * (mInitialScrollY + mZoomCenterY - titleHeight)
3032 - (mZoomCenterY - titleHeight));
3033 ty = -(ty <= titleHeight ? Math.max(ty, 0) : pinLoc(ty
3034 - titleHeight, getViewHeight(), Math.round(mContentHeight
3035 * zoomScale)) + titleHeight) + mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003036 canvas.translate(tx, ty);
3037 canvas.scale(zoomScale, zoomScale);
Leon Scroggins608f9f42009-09-03 10:06:04 -04003038 if (inEditingMode() && !mNeedToAdjustWebTextView
3039 && mZoomScale != 0) {
3040 // The WebTextView is up. Keep track of this so we can adjust
3041 // its size and placement when we finish zooming
3042 mNeedToAdjustWebTextView = true;
3043 // If it is in password mode, turn it off so it does not draw
3044 // misplaced.
3045 if (nativeFocusCandidateIsPassword()) {
3046 mWebTextView.setInPassword(false);
3047 }
3048 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003049 } else {
3050 canvas.scale(mActualScale, mActualScale);
3051 }
3052
3053 mWebViewCore.drawContentPicture(canvas, color, animateZoom,
3054 animateScroll);
3055
3056 if (mNativeClass == 0) return;
Cary Clarkbadd8392009-10-15 13:32:08 -04003057 if (mShiftIsPressed && !animateZoom) {
Cary Clark09e383c2009-10-26 16:43:58 -04003058 if (mTouchSelection || mExtendSelection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003059 nativeDrawSelectionRegion(canvas);
Cary Clark09e383c2009-10-26 16:43:58 -04003060 }
3061 if (!mTouchSelection) {
3062 nativeDrawSelectionPointer(canvas, mInvActualScale, mSelectX,
3063 mSelectY - getTitleHeight(), mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003064 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003065 } else if (drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003066 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3067 mTouchMode = TOUCH_SHORTPRESS_MODE;
3068 HitTestResult hitTest = getHitTestResult();
Grace Kloba5f68d6f2009-12-08 18:42:54 -08003069 if (mPreventLongPress || (hitTest != null &&
3070 hitTest.mType != HitTestResult.UNKNOWN_TYPE)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003071 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3072 .obtainMessage(SWITCH_TO_LONGPRESS),
3073 LONG_PRESS_TIMEOUT);
3074 }
3075 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003076 nativeDrawCursorRing(canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003077 }
3078 // When the FindDialog is up, only draw the matches if we are not in
3079 // the process of scrolling them into view.
3080 if (mFindIsUp && !animateScroll) {
3081 nativeDrawMatches(canvas);
3082 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04003083 if (mFocusSizeChanged) {
3084 mFocusSizeChanged = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003085 // If we are zooming, this will get handled above, when the zoom
3086 // finishes. We also do not need to do this unless the WebTextView
3087 // is showing.
3088 if (!animateZoom && inEditingMode()) {
3089 didUpdateTextViewBounds(true);
3090 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04003091 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003092 }
3093
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003094 // draw history
3095 private boolean mDrawHistory = false;
3096 private Picture mHistoryPicture = null;
3097 private int mHistoryWidth = 0;
3098 private int mHistoryHeight = 0;
3099
3100 // Only check the flag, can be called from WebCore thread
3101 boolean drawHistory() {
3102 return mDrawHistory;
3103 }
3104
3105 // Should only be called in UI thread
3106 void switchOutDrawHistory() {
3107 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Cary Clarkbc2e33b2009-04-23 13:12:19 -04003108 if (mDrawHistory && mWebViewCore.pictureReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003109 mDrawHistory = false;
3110 invalidate();
3111 int oldScrollX = mScrollX;
3112 int oldScrollY = mScrollY;
3113 mScrollX = pinLocX(mScrollX);
3114 mScrollY = pinLocY(mScrollY);
3115 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
3116 mUserScroll = false;
3117 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
3118 oldScrollY);
3119 }
3120 sendOurVisibleRect();
3121 }
3122 }
3123
Cary Clarkd6982c92009-05-29 11:02:22 -04003124 WebViewCore.CursorData cursorData() {
3125 WebViewCore.CursorData result = new WebViewCore.CursorData();
3126 result.mMoveGeneration = nativeMoveGeneration();
3127 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04003128 Point position = nativeCursorPosition();
3129 result.mX = position.x;
3130 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04003131 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003132 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003134 /**
3135 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04003136 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003137 * order, swap them.
3138 * @param start Beginning of selection to delete.
3139 * @param end End of selection to delete.
3140 */
3141 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003142 mTextGeneration++;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04003143 WebViewCore.TextSelectionData data
3144 = new WebViewCore.TextSelectionData(start, end);
3145 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
3146 data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003147 }
3148
3149 /**
3150 * Set the selection to (start, end) in the focused textfield. If start and
3151 * end are out of order, swap them.
3152 * @param start Beginning of selection.
3153 * @param end End of selection.
3154 */
3155 /* package */ void setSelection(int start, int end) {
Cary Clark2f1d60c2009-06-03 08:05:53 -04003156 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003157 }
3158
3159 // Called by JNI when a touch event puts a textfield into focus.
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003160 private void displaySoftKeyboard(boolean isTextView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003161 InputMethodManager imm = (InputMethodManager)
3162 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003163
3164 if (isTextView) {
Grace Klobab641abf2009-09-16 17:28:24 -07003165 if (mWebTextView == null) return;
3166
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003167 imm.showSoftInput(mWebTextView, 0);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003168 if (mInZoomOverview) {
3169 // if in zoom overview mode, call doDoubleTap() to bring it back
3170 // to normal mode so that user can enter text.
3171 doDoubleTap();
3172 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003173 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003174 else { // used by plugins
3175 imm.showSoftInput(this, 0);
3176 }
3177 }
3178
3179 // Called by WebKit to instruct the UI to hide the keyboard
3180 private void hideSoftKeyboard() {
3181 InputMethodManager imm = (InputMethodManager)
3182 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
3183
3184 imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003185 }
3186
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003187 /**
3188 * Only for calling from JNI. Allows a click on an unfocused textfield to
3189 * put the textfield in focus.
3190 */
3191 private void setOkayNotToMatch() {
3192 if (inEditingMode()) {
3193 mWebTextView.mOkayForFocusNotToMatch = true;
3194 }
3195 }
3196
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003197 /*
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003198 * This method checks the current focus and cursor and potentially rebuilds
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003199 * mWebTextView to have the appropriate properties, such as password,
3200 * multiline, and what text it contains. It also removes it if necessary.
3201 */
Leon Scroggins01058282009-07-30 16:33:56 -04003202 /* package */ void rebuildWebTextView() {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003203 // If the WebView does not have focus, do nothing until it gains focus.
Grace Kloba04b28682009-09-14 14:38:37 -07003204 if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003205 return;
3206 }
3207 boolean alreadyThere = inEditingMode();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003208 // inEditingMode can only return true if mWebTextView is non-null,
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003209 // so we can safely call remove() if (alreadyThere)
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003210 if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003211 if (alreadyThere) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003212 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003213 }
3214 return;
3215 }
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003216 // At this point, we know we have found an input field, so go ahead
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003217 // and create the WebTextView if necessary.
3218 if (mWebTextView == null) {
3219 mWebTextView = new WebTextView(mContext, WebView.this);
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003220 // Initialize our generation number.
3221 mTextGeneration = 0;
3222 }
Leon Scroggins3a6c88c2009-09-09 14:50:23 -04003223 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
3224 contentToViewDimension(nativeFocusCandidateTextSize()));
Cary Clark3524be92009-06-22 13:09:11 -04003225 Rect visibleRect = new Rect();
3226 calcOurContentVisibleRect(visibleRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003227 // Note that sendOurVisibleRect calls viewToContent, so the coordinates
3228 // should be in content coordinates.
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003229 Rect bounds = nativeFocusCandidateNodeBounds();
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003230 Rect vBox = contentToViewRect(bounds);
3231 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height());
Cary Clarkd6982c92009-05-29 11:02:22 -04003232 if (!Rect.intersects(bounds, visibleRect)) {
Leon Scroggins40981262009-07-01 10:57:47 -04003233 mWebTextView.bringIntoView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003234 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003235 String text = nativeFocusCandidateText();
3236 int nodePointer = nativeFocusCandidatePointer();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003237 if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003238 // It is possible that we have the same textfield, but it has moved,
3239 // i.e. In the case of opening/closing the screen.
3240 // In that case, we need to set the dimensions, but not the other
3241 // aspects.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003242 // If the text has been changed by webkit, update it. However, if
3243 // there has been more UI text input, ignore it. We will receive
3244 // another update when that text is recognized.
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003245 if (text != null && !text.equals(mWebTextView.getText().toString())
Cary Clarkd6982c92009-05-29 11:02:22 -04003246 && nativeTextGeneration() == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003247 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003248 }
3249 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003250 mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
3251 Gravity.RIGHT : Gravity.NO_GRAVITY);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003252 // This needs to be called before setType, which may call
3253 // requestFormData, and it needs to have the correct nodePointer.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003254 mWebTextView.setNodePointer(nodePointer);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003255 mWebTextView.setType(nativeFocusCandidateType());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003256 if (null == text) {
Cary Clark243ea062009-06-25 10:49:32 -04003257 if (DebugFlags.WEB_VIEW) {
3258 Log.v(LOGTAG, "rebuildWebTextView null == text");
3259 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003260 text = "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003261 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003262 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003263 }
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003264 mWebTextView.requestFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003265 }
3266
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003267 /**
3268 * Called by WebTextView to find saved form data associated with the
3269 * textfield
3270 * @param name Name of the textfield.
3271 * @param nodePointer Pointer to the node of the textfield, so it can be
3272 * compared to the currently focused textfield when the data is
3273 * retrieved.
3274 */
3275 /* package */ void requestFormData(String name, int nodePointer) {
3276 if (mWebViewCore.getSettings().getSaveFormData()) {
3277 Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
3278 update.arg1 = nodePointer;
3279 RequestFormData updater = new RequestFormData(name, getUrl(),
3280 update);
3281 Thread t = new Thread(updater);
3282 t.start();
3283 }
3284 }
3285
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003286 /*
3287 * This class requests an Adapter for the WebTextView which shows past
3288 * entries stored in the database. It is a Runnable so that it can be done
3289 * in its own thread, without slowing down the UI.
3290 */
3291 private class RequestFormData implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003292 private String mName;
3293 private String mUrl;
3294 private Message mUpdateMessage;
3295
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003296 public RequestFormData(String name, String url, Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003297 mName = name;
3298 mUrl = url;
3299 mUpdateMessage = msg;
3300 }
3301
3302 public void run() {
3303 ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
3304 if (pastEntries.size() > 0) {
3305 AutoCompleteAdapter adapter = new
3306 AutoCompleteAdapter(mContext, pastEntries);
Cary Clarkded054c2009-06-15 10:26:08 -04003307 mUpdateMessage.obj = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003308 mUpdateMessage.sendToTarget();
3309 }
3310 }
3311 }
3312
Grace Kloba8ae1b412009-11-23 10:35:34 -08003313 /**
3314 * Dump the display tree to "/sdcard/displayTree.txt"
3315 *
3316 * @hide debug only
3317 */
3318 public void dumpDisplayTree() {
3319 nativeDumpDisplayTree(getUrl());
3320 }
3321
3322 /**
3323 * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
3324 * "/sdcard/domTree.txt"
3325 *
3326 * @hide debug only
3327 */
3328 public void dumpDomTree(boolean toFile) {
3329 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
3330 }
3331
3332 /**
3333 * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
3334 * to "/sdcard/renderTree.txt"
3335 *
3336 * @hide debug only
3337 */
3338 public void dumpRenderTree(boolean toFile) {
3339 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
3340 }
3341
Leon Scrogginse3225672009-06-03 15:53:13 -04003342 // This is used to determine long press with the center key. Does not
3343 // affect long press with the trackball/touch.
3344 private boolean mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003345
3346 @Override
3347 public boolean onKeyDown(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003348 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003349 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003350 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003351 }
3352
3353 if (mNativeClass == 0) {
3354 return false;
3355 }
3356
3357 // do this hack up front, so it always works, regardless of touch-mode
3358 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
3359 mAutoRedraw = !mAutoRedraw;
3360 if (mAutoRedraw) {
3361 invalidate();
3362 }
3363 return true;
3364 }
3365
3366 // Bubble up the key event if
3367 // 1. it is a system key; or
Grace Kloba04b28682009-09-14 14:38:37 -07003368 // 2. the host application wants to handle it;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003369 if (event.isSystem()
Grace Kloba04b28682009-09-14 14:38:37 -07003370 || mCallbackProxy.uiOverrideKeyEvent(event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003371 return false;
3372 }
3373
Cary Clark2f1d60c2009-06-03 08:05:53 -04003374 if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
Cary Clarkd6982c92009-05-29 11:02:22 -04003375 && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003376 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
Cary Clark09e383c2009-10-26 16:43:58 -04003377 setUpSelectXY();
3378 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003379
3380 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3381 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3382 // always handle the navigation keys in the UI thread
3383 switchOutDrawHistory();
Cary Clarkc05af372009-10-16 10:52:27 -04003384 if (mShiftIsPressed) {
3385 int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
3386 ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
3387 int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
3388 -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0;
3389 int multiplier = event.getRepeatCount() + 1;
3390 moveSelection(xRate * multiplier, yRate * multiplier);
3391 return true;
3392 }
Cary Clark215b72c2009-06-26 14:38:43 -04003393 if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003394 playSoundEffect(keyCodeToSoundsEffect(keyCode));
3395 return true;
3396 }
3397 // Bubble up the key event as WebView doesn't handle it
3398 return false;
3399 }
3400
Leon Scrogginse3225672009-06-03 15:53:13 -04003401 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003402 switchOutDrawHistory();
3403 if (event.getRepeatCount() == 0) {
Cary Clarkc05af372009-10-16 10:52:27 -04003404 if (mShiftIsPressed) {
3405 return true; // discard press if copy in progress
3406 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003407 mGotCenterDown = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003408 mPrivateHandler.sendMessageDelayed(mPrivateHandler
Leon Scrogginse3225672009-06-03 15:53:13 -04003409 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003410 // Already checked mNativeClass, so we do not need to check it
3411 // again.
3412 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
3413 return true;
3414 }
3415 // Bubble up the key event as WebView doesn't handle it
3416 return false;
3417 }
3418
Cary Clark843bbb82009-04-20 16:03:31 -04003419 if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
3420 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
3421 // turn off copy select if a shift-key combo is pressed
3422 mExtendSelection = mShiftIsPressed = false;
3423 if (mTouchMode == TOUCH_SELECT_MODE) {
3424 mTouchMode = TOUCH_INIT_MODE;
3425 }
3426 }
3427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003428 if (getSettings().getNavDump()) {
3429 switch (keyCode) {
3430 case KeyEvent.KEYCODE_4:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003431 dumpDisplayTree();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003432 break;
3433 case KeyEvent.KEYCODE_5:
3434 case KeyEvent.KEYCODE_6:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003435 dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003436 break;
3437 case KeyEvent.KEYCODE_7:
3438 case KeyEvent.KEYCODE_8:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003439 dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003440 break;
3441 case KeyEvent.KEYCODE_9:
3442 nativeInstrumentReport();
3443 return true;
3444 }
3445 }
3446
Derek Sollenberger718d69f2009-10-19 15:56:43 -04003447 if (nativeCursorIsTextInput()) {
Leon Scroggins1cc24202009-06-16 10:10:28 -04003448 // This message will put the node in focus, for the DOM's notion
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003449 // of focus, and make the focuscontroller active
Leon Scroggins01058282009-07-30 16:33:56 -04003450 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
3451 nativeCursorNodePointer());
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003452 // This will bring up the WebTextView and put it in focus, for
3453 // our view system's notion of focus
3454 rebuildWebTextView();
3455 // Now we need to pass the event to it
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003456 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003457 mWebTextView.setDefaultSelection();
3458 mWebTextView.mOkayForFocusNotToMatch = true;
3459 return mWebTextView.dispatchKeyEvent(event);
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003460 }
Leon Scroggins40981262009-07-01 10:57:47 -04003461 } else if (nativeHasFocusNode()) {
3462 // In this case, the cursor is not on a text input, but the focus
3463 // might be. Check it, and if so, hand over to the WebTextView.
3464 rebuildWebTextView();
3465 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003466 return mWebTextView.dispatchKeyEvent(event);
Leon Scroggins40981262009-07-01 10:57:47 -04003467 }
Cary Clark19436562009-06-04 16:25:07 -04003468 }
3469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003470 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003471 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003472 // pass the key to DOM
3473 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
3474 // return true as DOM handles the key
3475 return true;
3476 }
3477
3478 // Bubble up the key event as WebView doesn't handle it
3479 return false;
3480 }
3481
3482 @Override
3483 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003484 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003485 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003486 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003487 }
3488
3489 if (mNativeClass == 0) {
3490 return false;
3491 }
3492
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003493 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04003494 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
3495 String text = nativeCursorText();
3496 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003497 && text.startsWith(SCHEME_TEL)) {
3498 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
3499 getContext().startActivity(intent);
3500 return true;
3501 }
3502 }
3503
3504 // Bubble up the key event if
3505 // 1. it is a system key; or
3506 // 2. the host application wants to handle it;
3507 if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
3508 return false;
3509 }
3510
Cary Clarkd6982c92009-05-29 11:02:22 -04003511 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003512 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
3513 if (commitCopy()) {
3514 return true;
3515 }
3516 }
3517
3518 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3519 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3520 // always handle the navigation keys in the UI thread
3521 // Bubble up the key event as WebView doesn't handle it
3522 return false;
3523 }
3524
Leon Scrogginse3225672009-06-03 15:53:13 -04003525 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003526 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04003527 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
3528 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003529
Leon Scrogginse3225672009-06-03 15:53:13 -04003530 if (mShiftIsPressed) {
Cary Clarkc05af372009-10-16 10:52:27 -04003531 if (mExtendSelection) {
3532 commitCopy();
3533 } else {
3534 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04003535 invalidate(); // draw the i-beam instead of the arrow
Cary Clarkc05af372009-10-16 10:52:27 -04003536 }
3537 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003538 }
Grace Klobadd817492009-09-14 10:17:06 -07003539
3540 // perform the single click
3541 Rect visibleRect = sendOurVisibleRect();
3542 // Note that sendOurVisibleRect calls viewToContent, so the
3543 // coordinates should be in content coordinates.
3544 if (!nativeCursorIntersects(visibleRect)) {
3545 return false;
3546 }
Grace Klobadd817492009-09-14 10:17:06 -07003547 WebViewCore.CursorData data = cursorData();
3548 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
3549 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003550 if (nativeCursorIsTextInput()) {
3551 rebuildWebTextView();
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003552 centerKeyPressOnTextField();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003553 if (inEditingMode()) {
3554 mWebTextView.setDefaultSelection();
3555 mWebTextView.mOkayForFocusNotToMatch = true;
3556 }
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003557 return true;
3558 }
3559 nativeSetFollowedLink(true);
3560 if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
Grace Klobadd817492009-09-14 10:17:06 -07003561 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
3562 nativeCursorNodePointer());
3563 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003564 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003565 }
3566
3567 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003568 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003569 // pass the key to DOM
3570 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
3571 // return true as DOM handles the key
3572 return true;
3573 }
3574
3575 // Bubble up the key event as WebView doesn't handle it
3576 return false;
3577 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003578
Cary Clark09e383c2009-10-26 16:43:58 -04003579 private void setUpSelectXY() {
3580 mExtendSelection = false;
3581 mShiftIsPressed = true;
3582 if (nativeHasCursorNode()) {
3583 Rect rect = nativeCursorNodeBounds();
3584 mSelectX = contentToViewX(rect.left);
3585 mSelectY = contentToViewY(rect.top);
3586 } else if (mLastTouchY > getVisibleTitleHeight()) {
3587 mSelectX = mScrollX + (int) mLastTouchX;
3588 mSelectY = mScrollY + (int) mLastTouchY;
3589 } else {
3590 mSelectX = mScrollX + getViewWidth() / 2;
3591 mSelectY = mScrollY + getViewHeightWithTitle() / 2;
3592 }
3593 nativeHideCursor();
3594 }
3595
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003596 /**
3597 * @hide
3598 */
3599 public void emulateShiftHeld() {
Cary Clark7f970112009-10-15 15:29:08 -04003600 if (0 == mNativeClass) return; // client isn't initialized
Cary Clark09e383c2009-10-26 16:43:58 -04003601 setUpSelectXY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003602 }
3603
3604 private boolean commitCopy() {
3605 boolean copiedSomething = false;
3606 if (mExtendSelection) {
3607 // copy region so core operates on copy without touching orig.
3608 Region selection = new Region(nativeGetSelection());
3609 if (selection.isEmpty() == false) {
3610 Toast.makeText(mContext
3611 , com.android.internal.R.string.text_copied
3612 , Toast.LENGTH_SHORT).show();
3613 mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection);
3614 copiedSomething = true;
3615 }
3616 mExtendSelection = false;
3617 }
3618 mShiftIsPressed = false;
Cary Clark09e383c2009-10-26 16:43:58 -04003619 invalidate(); // remove selection region and pointer
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003620 if (mTouchMode == TOUCH_SELECT_MODE) {
3621 mTouchMode = TOUCH_INIT_MODE;
3622 }
3623 return copiedSomething;
3624 }
3625
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003626 @Override
3627 protected void onAttachedToWindow() {
3628 super.onAttachedToWindow();
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003629 if (hasWindowFocus()) onWindowFocusChanged(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003630 }
3631
3632 @Override
3633 protected void onDetachedFromWindow() {
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003634 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003635 super.onDetachedFromWindow();
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003636 // Clean up the zoom controller
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003637 mZoomButtonsController.setVisible(false);
3638 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003639
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003640 /**
3641 * @deprecated WebView no longer needs to implement
3642 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
3643 */
3644 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003645 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04003646
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003647 /**
3648 * @deprecated WebView no longer needs to implement
3649 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
3650 */
3651 @Deprecated
3652 public void onChildViewRemoved(View p, View child) {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003653
3654 /**
3655 * @deprecated WebView should not have implemented
3656 * ViewTreeObserver.OnGlobalFocusChangeListener. This method
3657 * does nothing now.
3658 */
3659 @Deprecated
3660 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
3661 }
3662
Cary Clarkd6982c92009-05-29 11:02:22 -04003663 // To avoid drawing the cursor ring, and remove the TextView when our window
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003664 // loses focus.
3665 @Override
3666 public void onWindowFocusChanged(boolean hasWindowFocus) {
3667 if (hasWindowFocus) {
3668 if (hasFocus()) {
3669 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003670 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04003671 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003672 if (mNativeClass != 0) {
3673 nativeRecordButtons(true, false, true);
Leon Scroggins8cdad882009-06-30 08:47:04 -04003674 if (inEditingMode()) {
3675 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
3676 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003677 }
3678 } else {
3679 // If our window gained focus, but we do not have it, do not
Cary Clarkd6982c92009-05-29 11:02:22 -04003680 // draw the cursor ring.
3681 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003682 // We do not call nativeRecordButtons here because we assume
3683 // that when we lost focus, or window focus, it got called with
3684 // false for the first parameter
3685 }
3686 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07003687 if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003688 /*
3689 * The zoom controls come in their own window, so our window
Cary Clarkd6982c92009-05-29 11:02:22 -04003690 * loses focus. Our policy is to not draw the cursor ring if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003691 * our window is not focused, but this is an exception since
3692 * the user can still navigate the web page with the zoom
3693 * controls showing.
3694 */
Cary Clarkd6982c92009-05-29 11:02:22 -04003695 // If our window has lost focus, stop drawing the cursor ring
3696 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003697 }
3698 mGotKeyDown = false;
3699 mShiftIsPressed = false;
3700 if (mNativeClass != 0) {
3701 nativeRecordButtons(false, false, true);
3702 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003703 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003704 }
3705 invalidate();
3706 super.onWindowFocusChanged(hasWindowFocus);
3707 }
3708
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003709 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003710 * Pass a message to WebCore Thread, telling the WebCore::Page's
3711 * FocusController to be "inactive" so that it will
3712 * not draw the blinking cursor. It gets set to "active" to draw the cursor
3713 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003714 */
Leon Scroggins01058282009-07-30 16:33:56 -04003715 /* package */ void setFocusControllerInactive() {
Leon Scrogginsfd06bc82009-06-08 13:22:02 -04003716 // Do not need to also check whether mWebViewCore is null, because
3717 // mNativeClass is only set if mWebViewCore is non null
3718 if (mNativeClass == 0) return;
Leon Scroggins8cdad882009-06-30 08:47:04 -04003719 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003720 }
3721
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003722 @Override
3723 protected void onFocusChanged(boolean focused, int direction,
3724 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003725 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003726 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
3727 }
3728 if (focused) {
3729 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003730 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003731 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003732 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003733 if (mNativeClass != 0) {
3734 nativeRecordButtons(true, false, true);
3735 }
3736 //} else {
3737 // The WebView has gained focus while we do not have
3738 // windowfocus. When our window lost focus, we should have
3739 // called nativeRecordButtons(false...)
3740 }
3741 } else {
3742 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04003743 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003744 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003745 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003746 if (mNativeClass != 0) {
3747 nativeRecordButtons(false, false, true);
3748 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003749 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003750 }
3751 mGotKeyDown = false;
3752 }
3753
3754 super.onFocusChanged(focused, direction, previouslyFocusedRect);
3755 }
3756
Grace Kloba3f9faf42009-10-13 14:13:54 -07003757 /**
3758 * @hide
3759 */
3760 @Override
3761 protected boolean setFrame(int left, int top, int right, int bottom) {
3762 boolean changed = super.setFrame(left, top, right, bottom);
3763 if (!changed && mHeightCanMeasure) {
3764 // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
3765 // in WebViewCore after we get the first layout. We do call
3766 // requestLayout() when we get contentSizeChanged(). But the View
3767 // system won't call onSizeChanged if the dimension is not changed.
3768 // In this case, we need to call sendViewSizeZoom() explicitly to
3769 // notify the WebKit about the new dimensions.
3770 sendViewSizeZoom();
3771 }
3772 return changed;
3773 }
3774
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003775 @Override
3776 protected void onSizeChanged(int w, int h, int ow, int oh) {
3777 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003778 // Center zooming to the center of the screen.
Cary Clarka91874d2009-08-24 14:08:43 -04003779 if (mZoomScale == 0) { // unless we're already zooming
3780 mZoomCenterX = getViewWidth() * .5f;
3781 mZoomCenterY = getViewHeight() * .5f;
3782 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003783
Grace Klobaa4fa1072009-11-09 12:01:50 -08003784 // adjust the max viewport width depending on the view dimensions. This
3785 // is to ensure the scaling is not going insane. So do not shrink it if
3786 // the view size is temporarily smaller, e.g. when soft keyboard is up.
3787 int newMaxViewportWidth = (int) (Math.max(w, h) / DEFAULT_MIN_ZOOM_SCALE);
3788 if (newMaxViewportWidth > sMaxViewportWidth) {
3789 sMaxViewportWidth = newMaxViewportWidth;
3790 }
3791
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003792 // update mMinZoomScale if the minimum zoom scale is not fixed
3793 if (!mMinZoomScaleFixed) {
Grace Klobaba672802009-09-25 15:54:08 -07003794 // when change from narrow screen to wide screen, the new viewWidth
3795 // can be wider than the old content width. We limit the minimum
3796 // scale to 1.0f. The proper minimum scale will be calculated when
3797 // the new picture shows up.
3798 mMinZoomScale = Math.min(1.0f, (float) getViewWidth()
Grace Klobae397a882009-08-06 12:04:14 -07003799 / (mDrawHistory ? mHistoryPicture.getWidth()
Grace Klobaba672802009-09-25 15:54:08 -07003800 : mZoomOverviewWidth));
Grace Kloba16efce72009-11-10 15:49:03 -08003801 if (mInitialScaleInPercent > 0) {
3802 // limit the minZoomScale to the initialScale if it is set
3803 float initialScale = mInitialScaleInPercent / 100.0f;
3804 if (mMinZoomScale > initialScale) {
3805 mMinZoomScale = initialScale;
3806 }
3807 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003808 }
3809
Grace Kloba70b942d2009-12-11 19:33:04 -08003810 // onSizeChanged() is called during WebView layout. And any
3811 // requestLayout() is blocked during layout. As setNewZoomScale() will
3812 // call its child View to reposition itself through ViewManager's
3813 // scaleAll(), we need to post a Runnable to ensure requestLayout().
3814 post(new Runnable() {
3815 public void run() {
3816 // we always force, in case our height changed, in which case we
3817 // still want to send the notification over to webkit
3818 setNewZoomScale(mActualScale, true);
3819 }
3820 });
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003821 }
3822
3823 @Override
3824 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
3825 super.onScrollChanged(l, t, oldl, oldt);
Patrick Scott0a5ce012009-07-02 08:56:10 -04003826
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003827 sendOurVisibleRect();
3828 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003829
3830
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003831 @Override
3832 public boolean dispatchKeyEvent(KeyEvent event) {
3833 boolean dispatch = true;
3834
3835 if (!inEditingMode()) {
3836 if (event.getAction() == KeyEvent.ACTION_DOWN) {
3837 mGotKeyDown = true;
3838 } else {
3839 if (!mGotKeyDown) {
3840 /*
3841 * We got a key up for which we were not the recipient of
3842 * the original key down. Don't give it to the view.
3843 */
3844 dispatch = false;
3845 }
3846 mGotKeyDown = false;
3847 }
3848 }
3849
3850 if (dispatch) {
3851 return super.dispatchKeyEvent(event);
3852 } else {
3853 // We didn't dispatch, so let something else handle the key
3854 return false;
3855 }
3856 }
3857
3858 // Here are the snap align logic:
3859 // 1. If it starts nearly horizontally or vertically, snap align;
3860 // 2. If there is a dramitic direction change, let it go;
3861 // 3. If there is a same direction back and forth, lock it.
3862
3863 // adjustable parameters
3864 private int mMinLockSnapReverseDistance;
3865 private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
3866 private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
3867
3868 @Override
3869 public boolean onTouchEvent(MotionEvent ev) {
3870 if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
3871 return false;
3872 }
3873
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003874 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003875 Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
3876 + mTouchMode);
3877 }
3878
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003879 int action = ev.getAction();
3880 float x = ev.getX();
3881 float y = ev.getY();
3882 long eventTime = ev.getEventTime();
3883
3884 // Due to the touch screen edge effect, a touch closer to the edge
3885 // always snapped to the edge. As getViewWidth() can be different from
3886 // getWidth() due to the scrollbar, adjusting the point to match
3887 // getViewWidth(). Same applied to the height.
3888 if (x > getViewWidth() - 1) {
3889 x = getViewWidth() - 1;
3890 }
Grace Kloba8eff73f2009-09-24 09:34:32 -07003891 if (y > getViewHeightWithTitle() - 1) {
3892 y = getViewHeightWithTitle() - 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003893 }
3894
3895 // pass the touch events from UI thread to WebCore thread
Grace Kloba04b28682009-09-14 14:38:37 -07003896 if (mForwardTouchEvents && (action != MotionEvent.ACTION_MOVE
3897 || eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003898 WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
3899 ted.mAction = action;
Leon Scroggins0236e672009-09-02 21:12:08 -04003900 ted.mX = viewToContentX((int) x + mScrollX);
3901 ted.mY = viewToContentY((int) y + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003902 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
3903 mLastSentTouchTime = eventTime;
3904 }
3905
Cary Clark25415e22009-10-12 13:41:28 -04003906 float fDeltaX = mLastTouchX - x;
3907 float fDeltaY = mLastTouchY - y;
3908 int deltaX = (int) fDeltaX;
3909 int deltaY = (int) fDeltaY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003910
3911 switch (action) {
3912 case MotionEvent.ACTION_DOWN: {
Grace Klobad66d84f2009-09-27 14:48:07 -07003913 mPreventDrag = PREVENT_DRAG_NO;
Grace Kloba04b28682009-09-14 14:38:37 -07003914 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04003915 // stop the current scroll animation, but if this is
3916 // the start of a fling, allow it to add to the current
3917 // fling's velocity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003918 mScroller.abortAnimation();
3919 mTouchMode = TOUCH_DRAG_START_MODE;
3920 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
3921 } else if (mShiftIsPressed) {
3922 mSelectX = mScrollX + (int) x;
3923 mSelectY = mScrollY + (int) y;
3924 mTouchMode = TOUCH_SELECT_MODE;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003925 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003926 Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
3927 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003928 nativeMoveSelection(viewToContentX(mSelectX),
3929 viewToContentY(mSelectY), false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003930 mTouchSelection = mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04003931 invalidate(); // draw the i-beam instead of the arrow
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003932 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
3933 mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
3934 if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
3935 mTouchMode = TOUCH_DOUBLE_TAP_MODE;
3936 } else {
3937 // commit the short press action for the previous tap
3938 doShortPress();
3939 // continue, mTouchMode should be still TOUCH_INIT_MODE
3940 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003941 } else {
3942 mTouchMode = TOUCH_INIT_MODE;
Grace Klobaf58af622009-09-24 17:41:23 -07003943 mPreventDrag = mForwardTouchEvents ? PREVENT_DRAG_MAYBE_YES
3944 : PREVENT_DRAG_NO;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08003945 mPreventLongPress = false;
3946 mPreventDoubleTap = false;
Cary Clark77d98f42009-07-31 09:40:38 -04003947 mWebViewCore.sendMessage(
3948 EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003949 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
3950 EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
3951 (eventTime - mLastTouchUpTime), eventTime);
3952 }
3953 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003954 // Trigger the link
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003955 if (mTouchMode == TOUCH_INIT_MODE
3956 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003957 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3958 .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
3959 }
3960 // Remember where the motion event started
3961 mLastTouchX = x;
3962 mLastTouchY = y;
3963 mLastTouchTime = eventTime;
3964 mVelocityTracker = VelocityTracker.obtain();
3965 mSnapScrollMode = SNAP_NONE;
3966 break;
3967 }
3968 case MotionEvent.ACTION_MOVE: {
Grace Kloba04b28682009-09-14 14:38:37 -07003969 if (mTouchMode == TOUCH_DONE_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003970 // no dragging during scroll zoom animation
3971 break;
3972 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003973 mVelocityTracker.addMovement(ev);
3974
3975 if (mTouchMode != TOUCH_DRAG_MODE) {
3976 if (mTouchMode == TOUCH_SELECT_MODE) {
3977 mSelectX = mScrollX + (int) x;
3978 mSelectY = mScrollY + (int) y;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003979 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003980 Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
3981 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003982 nativeMoveSelection(viewToContentX(mSelectX),
3983 viewToContentY(mSelectY), true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003984 invalidate();
3985 break;
3986 }
Grace Klobaf58af622009-09-24 17:41:23 -07003987 if ((deltaX * deltaX + deltaY * deltaY) < mTouchSlopSquare) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003988 break;
3989 }
Grace Klobaf58af622009-09-24 17:41:23 -07003990 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
3991 // track mLastTouchTime as we may need to do fling at
3992 // ACTION_UP
3993 mLastTouchTime = eventTime;
3994 break;
3995 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003996 if (mTouchMode == TOUCH_SHORTPRESS_MODE
3997 || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3998 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003999 } else if (mTouchMode == TOUCH_INIT_MODE
4000 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004001 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4002 }
Grace Kloba11438c32009-12-16 11:39:12 -08004003 if (mFullScreenHolder != null) {
4004 // in full screen mode, the WebView can't be panned.
4005 mTouchMode = TOUCH_DONE_MODE;
4006 break;
4007 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004008
4009 // if it starts nearly horizontal or vertical, enforce it
4010 int ax = Math.abs(deltaX);
4011 int ay = Math.abs(deltaY);
4012 if (ax > MAX_SLOPE_FOR_DIAG * ay) {
4013 mSnapScrollMode = SNAP_X;
4014 mSnapPositive = deltaX > 0;
4015 } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
4016 mSnapScrollMode = SNAP_Y;
4017 mSnapPositive = deltaY > 0;
4018 }
4019
4020 mTouchMode = TOUCH_DRAG_MODE;
Romain Guyf7b4acc2009-12-01 16:24:45 -08004021 mLastTouchX = x;
4022 mLastTouchY = y;
4023 fDeltaX = 0.0f;
4024 fDeltaY = 0.0f;
4025 deltaX = 0;
4026 deltaY = 0;
4027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004028 WebViewCore.pauseUpdate(mWebViewCore);
Leon Scroggins72543e12009-07-23 15:29:45 -04004029 if (!mDragFromTextInput) {
4030 nativeHideCursor();
4031 }
The Android Open Source Project10592532009-03-18 17:39:46 -07004032 WebSettings settings = getSettings();
Grace Kloba455e3af2009-08-13 11:01:21 -07004033 if (settings.supportZoom()
The Android Open Source Project10592532009-03-18 17:39:46 -07004034 && settings.getBuiltInZoomControls()
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004035 && !mZoomButtonsController.isVisible()
Grace Kloba04b28682009-09-14 14:38:37 -07004036 && mMinZoomScale < mMaxZoomScale) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004037 mZoomButtonsController.setVisible(true);
Grace Klobaf8d8b462009-09-20 15:57:49 -07004038 int count = settings.getDoubleTapToastCount();
4039 if (mInZoomOverview && count > 0) {
Grace Kloba24a3ff92009-09-22 10:42:22 -07004040 settings.setDoubleTapToastCount(--count);
Grace Klobaf8d8b462009-09-20 15:57:49 -07004041 Toast.makeText(mContext,
4042 com.android.internal.R.string.double_tap_toast,
Grace Kloba24a3ff92009-09-22 10:42:22 -07004043 Toast.LENGTH_LONG).show();
Grace Klobaf8d8b462009-09-20 15:57:49 -07004044 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004045 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004046 }
4047
4048 // do pan
4049 int newScrollX = pinLocX(mScrollX + deltaX);
Cary Clark25415e22009-10-12 13:41:28 -04004050 int newDeltaX = newScrollX - mScrollX;
4051 if (deltaX != newDeltaX) {
4052 deltaX = newDeltaX;
4053 fDeltaX = (float) newDeltaX;
4054 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004055 int newScrollY = pinLocY(mScrollY + deltaY);
Cary Clark25415e22009-10-12 13:41:28 -04004056 int newDeltaY = newScrollY - mScrollY;
4057 if (deltaY != newDeltaY) {
4058 deltaY = newDeltaY;
4059 fDeltaY = (float) newDeltaY;
4060 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004061 boolean done = false;
Cary Clark25415e22009-10-12 13:41:28 -04004062 boolean keepScrollBarsVisible = false;
4063 if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
4064 keepScrollBarsVisible = done = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004065 } else {
4066 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
4067 int ax = Math.abs(deltaX);
4068 int ay = Math.abs(deltaY);
4069 if (mSnapScrollMode == SNAP_X) {
4070 // radical change means getting out of snap mode
4071 if (ay > MAX_SLOPE_FOR_DIAG * ax
4072 && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
4073 mSnapScrollMode = SNAP_NONE;
4074 }
4075 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004076 if (ax > MAX_SLOPE_FOR_DIAG * ay &&
4077 (mSnapPositive
4078 ? deltaX < -mMinLockSnapReverseDistance
4079 : deltaX > mMinLockSnapReverseDistance)) {
4080 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004081 }
4082 } else {
4083 // radical change means getting out of snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004084 if (ax > MAX_SLOPE_FOR_DIAG * ay
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004085 && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
4086 mSnapScrollMode = SNAP_NONE;
4087 }
4088 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004089 if (ay > MAX_SLOPE_FOR_DIAG * ax &&
4090 (mSnapPositive
4091 ? deltaY < -mMinLockSnapReverseDistance
4092 : deltaY > mMinLockSnapReverseDistance)) {
4093 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004094 }
4095 }
4096 }
Cary Clarkac492e12009-10-14 14:53:37 -04004097 if (mSnapScrollMode != SNAP_NONE) {
4098 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
4099 deltaY = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07004100 } else {
Cary Clarkac492e12009-10-14 14:53:37 -04004101 deltaX = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07004102 }
Cary Clarkac492e12009-10-14 14:53:37 -04004103 }
4104 if ((deltaX | deltaY) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004105 scrollBy(deltaX, deltaY);
Cary Clarkac492e12009-10-14 14:53:37 -04004106 if (deltaX != 0) {
4107 mLastTouchX = x;
4108 }
4109 if (deltaY != 0) {
4110 mLastTouchY = y;
4111 }
4112 mHeldMotionless = MOTIONLESS_FALSE;
4113 } else {
4114 // keep the scrollbar on the screen even there is no
4115 // scroll
4116 keepScrollBarsVisible = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004117 }
4118 mLastTouchTime = eventTime;
4119 mUserScroll = true;
4120 }
The Android Open Source Project10592532009-03-18 17:39:46 -07004121
Grace Kloba455e3af2009-08-13 11:01:21 -07004122 if (!getSettings().getBuiltInZoomControls()) {
The Android Open Source Project10592532009-03-18 17:39:46 -07004123 boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
Grace Kloba04b28682009-09-14 14:38:37 -07004124 if (mZoomControls != null && showPlusMinus) {
The Android Open Source Project10592532009-03-18 17:39:46 -07004125 if (mZoomControls.getVisibility() == View.VISIBLE) {
4126 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4127 } else {
Grace Kloba04b28682009-09-14 14:38:37 -07004128 mZoomControls.show(showPlusMinus, false);
The Android Open Source Project10592532009-03-18 17:39:46 -07004129 }
4130 mPrivateHandler.postDelayed(mZoomControlRunnable,
4131 ZOOM_CONTROLS_TIMEOUT);
4132 }
4133 }
4134
Cary Clark25415e22009-10-12 13:41:28 -04004135 if (keepScrollBarsVisible) {
4136 if (mHeldMotionless != MOTIONLESS_TRUE) {
4137 mHeldMotionless = MOTIONLESS_TRUE;
4138 invalidate();
4139 }
Grace Kloba5b2c0562009-09-30 10:17:13 -07004140 // keep the scrollbar on the screen even there is no scroll
4141 awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
4142 false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004143 // return false to indicate that we can't pan out of the
4144 // view space
Cary Clark25415e22009-10-12 13:41:28 -04004145 return !done;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004146 }
4147 break;
4148 }
4149 case MotionEvent.ACTION_UP: {
4150 mLastTouchUpTime = eventTime;
4151 switch (mTouchMode) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004152 case TOUCH_DOUBLE_TAP_MODE: // double tap
4153 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad3c6d542009-07-31 14:24:19 -07004154 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004155 if (mPreventDoubleTap) {
4156 WebViewCore.TouchEventData ted
4157 = new WebViewCore.TouchEventData();
4158 ted.mAction = WebViewCore.ACTION_DOUBLETAP;
4159 ted.mX = viewToContentX((int) x + mScrollX);
4160 ted.mY = viewToContentY((int) y + mScrollY);
4161 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Grace Kloba11438c32009-12-16 11:39:12 -08004162 } else if (mFullScreenHolder == null) {
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004163 doDoubleTap();
4164 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004165 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004166 case TOUCH_SELECT_MODE:
4167 commitCopy();
4168 mTouchSelection = false;
4169 break;
Grace Klobaf58af622009-09-24 17:41:23 -07004170 case TOUCH_INIT_MODE: // tap
Grace Klobad66d84f2009-09-27 14:48:07 -07004171 case TOUCH_SHORTPRESS_START_MODE:
4172 case TOUCH_SHORTPRESS_MODE:
Grace Klobaf58af622009-09-24 17:41:23 -07004173 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad66d84f2009-09-27 14:48:07 -07004174 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Klobaf58af622009-09-24 17:41:23 -07004175 if ((deltaX * deltaX + deltaY * deltaY) > mTouchSlopSquare) {
4176 Log.w(LOGTAG, "Miss a drag as we are waiting for" +
4177 " WebCore's response for touch down.");
Grace Kloba11438c32009-12-16 11:39:12 -08004178 if (mFullScreenHolder == null
4179 && (computeHorizontalScrollExtent() < computeHorizontalScrollRange()
4180 || computeVerticalScrollExtent() < computeVerticalScrollRange())) {
Grace Klobaf58af622009-09-24 17:41:23 -07004181 // we will not rewrite drag code here, but we
4182 // will try fling if it applies.
4183 WebViewCore.pauseUpdate(mWebViewCore);
4184 // fall through to TOUCH_DRAG_MODE
4185 } else {
4186 break;
4187 }
4188 } else {
4189 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
4190 // if mPreventDrag is not confirmed, treat it as
4191 // no so that it won't block tap or double tap.
4192 mPreventDrag = PREVENT_DRAG_NO;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004193 mPreventLongPress = false;
4194 mPreventDoubleTap = false;
Grace Klobaf58af622009-09-24 17:41:23 -07004195 }
4196 if (mPreventDrag == PREVENT_DRAG_NO) {
Grace Klobad66d84f2009-09-27 14:48:07 -07004197 if (mTouchMode == TOUCH_INIT_MODE) {
4198 mPrivateHandler.sendMessageDelayed(
4199 mPrivateHandler.obtainMessage(
4200 RELEASE_SINGLE_TAP),
4201 ViewConfiguration.getDoubleTapTimeout());
4202 } else {
4203 mTouchMode = TOUCH_DONE_MODE;
4204 doShortPress();
4205 }
Grace Klobaf58af622009-09-24 17:41:23 -07004206 }
4207 break;
4208 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004209 case TOUCH_DRAG_MODE:
Cary Clark25415e22009-10-12 13:41:28 -04004210 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4211 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
4212 mHeldMotionless = MOTIONLESS_TRUE;
Mike Reeddf4cf292009-09-15 14:31:54 -04004213 // redraw in high-quality, as we're done dragging
4214 invalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004215 // if the user waits a while w/o moving before the
4216 // up, we don't want to do a fling
4217 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
4218 mVelocityTracker.addMovement(ev);
4219 doFling();
4220 break;
4221 }
Cary Clark278ce052009-08-31 16:08:42 -04004222 mLastVelocity = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004223 WebViewCore.resumeUpdate(mWebViewCore);
4224 break;
4225 case TOUCH_DRAG_START_MODE:
4226 case TOUCH_DONE_MODE:
4227 // do nothing
4228 break;
4229 }
4230 // we also use mVelocityTracker == null to tell us that we are
4231 // not "moving around", so we can take the slower/prettier
4232 // mode in the drawing code
4233 if (mVelocityTracker != null) {
4234 mVelocityTracker.recycle();
4235 mVelocityTracker = null;
4236 }
4237 break;
4238 }
4239 case MotionEvent.ACTION_CANCEL: {
4240 // we also use mVelocityTracker == null to tell us that we are
4241 // not "moving around", so we can take the slower/prettier
4242 // mode in the drawing code
4243 if (mVelocityTracker != null) {
4244 mVelocityTracker.recycle();
4245 mVelocityTracker = null;
4246 }
Grace Kloba04b28682009-09-14 14:38:37 -07004247 if (mTouchMode == TOUCH_DRAG_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004248 WebViewCore.resumeUpdate(mWebViewCore);
4249 }
4250 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4251 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Cary Clark25415e22009-10-12 13:41:28 -04004252 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4253 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
4254 mHeldMotionless = MOTIONLESS_TRUE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004255 mTouchMode = TOUCH_DONE_MODE;
Cary Clarke872f3a2009-06-11 09:51:11 -04004256 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004257 break;
4258 }
4259 }
4260 return true;
4261 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004262
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004263 private long mTrackballFirstTime = 0;
4264 private long mTrackballLastTime = 0;
4265 private float mTrackballRemainsX = 0.0f;
4266 private float mTrackballRemainsY = 0.0f;
4267 private int mTrackballXMove = 0;
4268 private int mTrackballYMove = 0;
4269 private boolean mExtendSelection = false;
4270 private boolean mTouchSelection = false;
4271 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
4272 private static final int TRACKBALL_TIMEOUT = 200;
4273 private static final int TRACKBALL_WAIT = 100;
4274 private static final int TRACKBALL_SCALE = 400;
4275 private static final int TRACKBALL_SCROLL_COUNT = 5;
4276 private static final int TRACKBALL_MOVE_COUNT = 10;
4277 private static final int TRACKBALL_MULTIPLIER = 3;
4278 private static final int SELECT_CURSOR_OFFSET = 16;
4279 private int mSelectX = 0;
4280 private int mSelectY = 0;
Cary Clark5da9aeb2009-10-06 17:40:53 -04004281 private boolean mFocusSizeChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004282 private boolean mShiftIsPressed = false;
4283 private boolean mTrackballDown = false;
4284 private long mTrackballUpTime = 0;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004285 private long mLastCursorTime = 0;
4286 private Rect mLastCursorBounds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004287
4288 // Set by default; BrowserActivity clears to interpret trackball data
Cary Clarkd6982c92009-05-29 11:02:22 -04004289 // directly for movement. Currently, the framework only passes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004290 // arrow key events, not trackball events, from one child to the next
4291 private boolean mMapTrackballToArrowKeys = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004292
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004293 public void setMapTrackballToArrowKeys(boolean setMap) {
4294 mMapTrackballToArrowKeys = setMap;
4295 }
4296
4297 void resetTrackballTime() {
4298 mTrackballLastTime = 0;
4299 }
4300
4301 @Override
4302 public boolean onTrackballEvent(MotionEvent ev) {
4303 long time = ev.getEventTime();
4304 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
4305 if (ev.getY() > 0) pageDown(true);
4306 if (ev.getY() < 0) pageUp(true);
4307 return true;
4308 }
4309 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Cary Clarkbadd8392009-10-15 13:32:08 -04004310 if (mShiftIsPressed) {
4311 return true; // discard press if copy in progress
4312 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004313 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004314 if (mNativeClass == 0) {
4315 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004316 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004317 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004318 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
4319 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
4320 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004321 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004322 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004323 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004324 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004325 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004326 }
4327 if (isInTouchMode()) requestFocusFromTouch();
4328 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04004329 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004330 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04004331 // LONG_PRESS_CENTER is set in common onKeyDown
4332 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004333 mTrackballDown = false;
4334 mTrackballUpTime = time;
4335 if (mShiftIsPressed) {
4336 if (mExtendSelection) {
4337 commitCopy();
4338 } else {
4339 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04004340 invalidate(); // draw the i-beam instead of the arrow
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004341 }
Cary Clarkbadd8392009-10-15 13:32:08 -04004342 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004343 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004344 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004345 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004346 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004347 );
4348 }
4349 return false; // let common code in onKeyUp at it
4350 }
4351 if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004352 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004353 return false;
4354 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004355 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004356 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004357 return true; // discard move if trackball is down
4358 }
4359 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004360 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004361 return true;
4362 }
4363 // TODO: alternatively we can do panning as touch does
4364 switchOutDrawHistory();
4365 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004366 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004367 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004368 + time + " last=" + mTrackballLastTime);
4369 }
4370 mTrackballFirstTime = time;
4371 mTrackballXMove = mTrackballYMove = 0;
4372 }
4373 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004374 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004375 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
4376 }
4377 mTrackballRemainsX += ev.getX();
4378 mTrackballRemainsY += ev.getY();
4379 doTrackball(time);
4380 return true;
4381 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004383 void moveSelection(float xRate, float yRate) {
4384 if (mNativeClass == 0)
4385 return;
4386 int width = getViewWidth();
4387 int height = getViewHeight();
Cary Clarkc05af372009-10-16 10:52:27 -04004388 mSelectX += xRate;
4389 mSelectY += yRate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004390 int maxX = width + mScrollX;
4391 int maxY = height + mScrollY;
4392 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
4393 , mSelectX));
4394 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
4395 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004396 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004397 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004398 + " mSelectX=" + mSelectX
4399 + " mSelectY=" + mSelectY
4400 + " mScrollX=" + mScrollX
4401 + " mScrollY=" + mScrollY
4402 + " xRate=" + xRate
4403 + " yRate=" + yRate
4404 );
4405 }
Leon Scroggins0236e672009-09-02 21:12:08 -04004406 nativeMoveSelection(viewToContentX(mSelectX),
4407 viewToContentY(mSelectY), mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004408 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004409 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004410 : 0;
4411 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004412 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004413 : 0;
4414 pinScrollBy(scrollX, scrollY, true, 0);
4415 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
4416 requestRectangleOnScreen(select);
4417 invalidate();
4418 }
4419
4420 private int scaleTrackballX(float xRate, int width) {
4421 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
4422 int nextXMove = xMove;
4423 if (xMove > 0) {
4424 if (xMove > mTrackballXMove) {
4425 xMove -= mTrackballXMove;
4426 }
4427 } else if (xMove < mTrackballXMove) {
4428 xMove -= mTrackballXMove;
4429 }
4430 mTrackballXMove = nextXMove;
4431 return xMove;
4432 }
4433
4434 private int scaleTrackballY(float yRate, int height) {
4435 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
4436 int nextYMove = yMove;
4437 if (yMove > 0) {
4438 if (yMove > mTrackballYMove) {
4439 yMove -= mTrackballYMove;
4440 }
4441 } else if (yMove < mTrackballYMove) {
4442 yMove -= mTrackballYMove;
4443 }
4444 mTrackballYMove = nextYMove;
4445 return yMove;
4446 }
4447
4448 private int keyCodeToSoundsEffect(int keyCode) {
4449 switch(keyCode) {
4450 case KeyEvent.KEYCODE_DPAD_UP:
4451 return SoundEffectConstants.NAVIGATION_UP;
4452 case KeyEvent.KEYCODE_DPAD_RIGHT:
4453 return SoundEffectConstants.NAVIGATION_RIGHT;
4454 case KeyEvent.KEYCODE_DPAD_DOWN:
4455 return SoundEffectConstants.NAVIGATION_DOWN;
4456 case KeyEvent.KEYCODE_DPAD_LEFT:
4457 return SoundEffectConstants.NAVIGATION_LEFT;
4458 }
4459 throw new IllegalArgumentException("keyCode must be one of " +
4460 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
4461 "KEYCODE_DPAD_LEFT}.");
4462 }
4463
4464 private void doTrackball(long time) {
4465 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
4466 if (elapsed == 0) {
4467 elapsed = TRACKBALL_TIMEOUT;
4468 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004469 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004470 float yRate = mTrackballRemainsY * 1000 / elapsed;
Cary Clarkc05af372009-10-16 10:52:27 -04004471 int viewWidth = getViewWidth();
4472 int viewHeight = getViewHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004473 if (mShiftIsPressed) {
Cary Clarkc05af372009-10-16 10:52:27 -04004474 moveSelection(scaleTrackballX(xRate, viewWidth),
4475 scaleTrackballY(yRate, viewHeight));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004476 mTrackballRemainsX = mTrackballRemainsY = 0;
4477 return;
4478 }
4479 float ax = Math.abs(xRate);
4480 float ay = Math.abs(yRate);
4481 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004482 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004483 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
4484 + " xRate=" + xRate
4485 + " yRate=" + yRate
4486 + " mTrackballRemainsX=" + mTrackballRemainsX
4487 + " mTrackballRemainsY=" + mTrackballRemainsY);
4488 }
Cary Clarkc05af372009-10-16 10:52:27 -04004489 int width = mContentWidth - viewWidth;
4490 int height = mContentHeight - viewHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004491 if (width < 0) width = 0;
4492 if (height < 0) height = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004493 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
4494 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
4495 maxA = Math.max(ax, ay);
4496 int count = Math.max(0, (int) maxA);
4497 int oldScrollX = mScrollX;
4498 int oldScrollY = mScrollY;
4499 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004500 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
4501 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004502 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
4503 KeyEvent.KEYCODE_DPAD_RIGHT;
4504 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004505 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004506 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004507 + " count=" + count
4508 + " mTrackballRemainsX=" + mTrackballRemainsX
4509 + " mTrackballRemainsY=" + mTrackballRemainsY);
4510 }
Cary Clark215b72c2009-06-26 14:38:43 -04004511 if (navHandledKey(selectKeyCode, count, false, time, false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004512 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
4513 }
4514 mTrackballRemainsX = mTrackballRemainsY = 0;
4515 }
4516 if (count >= TRACKBALL_SCROLL_COUNT) {
4517 int xMove = scaleTrackballX(xRate, width);
4518 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004519 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004520 Log.v(LOGTAG, "doTrackball pinScrollBy"
4521 + " count=" + count
4522 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04004523 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
4524 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004525 );
4526 }
4527 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
4528 xMove = 0;
4529 }
4530 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
4531 yMove = 0;
4532 }
4533 if (xMove != 0 || yMove != 0) {
4534 pinScrollBy(xMove, yMove, true, 0);
4535 }
4536 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004537 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004538 }
4539
Mike Reede8853fc2009-09-04 14:01:48 -04004540 private int computeMaxScrollY() {
Grace Klobae621d6f2009-09-11 13:20:39 -07004541 int maxContentH = computeVerticalScrollRange() + getTitleHeight();
Grace Klobabf5b6322009-11-10 15:34:34 -08004542 return Math.max(maxContentH - getViewHeightWithTitle(), getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04004543 }
4544
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004545 public void flingScroll(int vx, int vy) {
4546 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04004547 int maxY = computeMaxScrollY();
Cary Clarkd6982c92009-05-29 11:02:22 -04004548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004549 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
4550 invalidate();
4551 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004552
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004553 private void doFling() {
4554 if (mVelocityTracker == null) {
4555 return;
4556 }
4557 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04004558 int maxY = computeMaxScrollY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004559
Romain Guy4296fc42009-07-06 11:48:52 -07004560 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004561 int vx = (int) mVelocityTracker.getXVelocity();
4562 int vy = (int) mVelocityTracker.getYVelocity();
4563
4564 if (mSnapScrollMode != SNAP_NONE) {
Cary Clarkac492e12009-10-14 14:53:37 -04004565 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004566 vy = 0;
4567 } else {
4568 vx = 0;
4569 }
4570 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004571
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004572 if (true /* EMG release: make our fling more like Maps' */) {
4573 // maps cuts their velocity in half
4574 vx = vx * 3 / 4;
4575 vy = vy * 3 / 4;
4576 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04004577 if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
4578 WebViewCore.resumeUpdate(mWebViewCore);
4579 return;
4580 }
Cary Clark278ce052009-08-31 16:08:42 -04004581 float currentVelocity = mScroller.getCurrVelocity();
4582 if (mLastVelocity > 0 && currentVelocity > 0) {
4583 float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
4584 - Math.atan2(vy, vx)));
4585 final float circle = (float) (Math.PI) * 2.0f;
4586 if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
4587 vx += currentVelocity * mLastVelX / mLastVelocity;
4588 vy += currentVelocity * mLastVelY / mLastVelocity;
4589 if (DebugFlags.WEB_VIEW) {
4590 Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
4591 }
4592 } else if (DebugFlags.WEB_VIEW) {
4593 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
4594 }
4595 } else if (DebugFlags.WEB_VIEW) {
4596 Log.v(LOGTAG, "doFling start last=" + mLastVelocity
Cary Clarkaa7caa62009-09-08 14:15:07 -04004597 + " current=" + currentVelocity
4598 + " vx=" + vx + " vy=" + vy
4599 + " maxX=" + maxX + " maxY=" + maxY
4600 + " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
Cary Clark278ce052009-08-31 16:08:42 -04004601 }
4602 mLastVelX = vx;
4603 mLastVelY = vy;
4604 mLastVelocity = (float) Math.hypot(vx, vy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004605
4606 mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
4607 // TODO: duration is calculated based on velocity, if the range is
4608 // small, the animation will stop before duration is up. We may
4609 // want to calculate how long the animation is going to run to precisely
4610 // resume the webcore update.
4611 final int time = mScroller.getDuration();
4612 mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_UPDATE, time);
Mike Cleronf116bf82009-09-27 19:14:12 -07004613 awakenScrollBars(time);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004614 invalidate();
4615 }
4616
4617 private boolean zoomWithPreview(float scale) {
4618 float oldScale = mActualScale;
Grace Kloba675c7d22009-07-23 09:21:21 -07004619 mInitialScrollX = mScrollX;
4620 mInitialScrollY = mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004621
Grace Kloba25737912009-06-19 12:42:47 -07004622 // snap to DEFAULT_SCALE if it is close
Grace Kloba0d8b77c2009-06-25 11:20:51 -07004623 if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
4624 scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004625 }
4626
4627 setNewZoomScale(scale, false);
4628
4629 if (oldScale != mActualScale) {
4630 // use mZoomPickerScale to see zoom preview first
4631 mZoomStart = SystemClock.uptimeMillis();
4632 mInvInitialZoomScale = 1.0f / oldScale;
4633 mInvFinalZoomScale = 1.0f / mActualScale;
4634 mZoomScale = mActualScale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004635 if (!mInZoomOverview) {
4636 mLastScale = scale;
4637 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004638 invalidate();
4639 return true;
4640 } else {
4641 return false;
4642 }
4643 }
4644
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004645 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004646 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
4647 * in charge of installing this view to the view hierarchy. This view will
4648 * become visible when the user starts scrolling via touch and fade away if
4649 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004650 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07004651 * API version 3 introduces a built-in zoom mechanism that is shown
4652 * automatically by the MapView. This is the preferred approach for
4653 * showing the zoom UI.
4654 *
4655 * @deprecated The built-in zoom mechanism is preferred, see
4656 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004657 */
The Android Open Source Project10592532009-03-18 17:39:46 -07004658 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004659 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07004660 if (!getSettings().supportZoom()) {
4661 Log.w(LOGTAG, "This WebView doesn't support zoom.");
4662 return null;
4663 }
4664 if (mZoomControls == null) {
4665 mZoomControls = createZoomControls();
Cary Clarkd6982c92009-05-29 11:02:22 -04004666
The Android Open Source Project10592532009-03-18 17:39:46 -07004667 /*
4668 * need to be set to VISIBLE first so that getMeasuredHeight() in
4669 * {@link #onSizeChanged()} can return the measured value for proper
4670 * layout.
4671 */
4672 mZoomControls.setVisibility(View.VISIBLE);
4673 mZoomControlRunnable = new Runnable() {
4674 public void run() {
Cary Clarkd6982c92009-05-29 11:02:22 -04004675
The Android Open Source Project10592532009-03-18 17:39:46 -07004676 /* Don't dismiss the controls if the user has
4677 * focus on them. Wait and check again later.
4678 */
4679 if (!mZoomControls.hasFocus()) {
4680 mZoomControls.hide();
4681 } else {
4682 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4683 mPrivateHandler.postDelayed(mZoomControlRunnable,
4684 ZOOM_CONTROLS_TIMEOUT);
4685 }
4686 }
4687 };
4688 }
4689 return mZoomControls;
4690 }
4691
4692 private ExtendedZoomControls createZoomControls() {
4693 ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
4694 , null);
4695 zoomControls.setOnZoomInClickListener(new OnClickListener() {
4696 public void onClick(View v) {
4697 // reset time out
4698 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4699 mPrivateHandler.postDelayed(mZoomControlRunnable,
4700 ZOOM_CONTROLS_TIMEOUT);
4701 zoomIn();
4702 }
4703 });
4704 zoomControls.setOnZoomOutClickListener(new OnClickListener() {
4705 public void onClick(View v) {
4706 // reset time out
4707 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4708 mPrivateHandler.postDelayed(mZoomControlRunnable,
4709 ZOOM_CONTROLS_TIMEOUT);
4710 zoomOut();
4711 }
4712 });
The Android Open Source Project10592532009-03-18 17:39:46 -07004713 return zoomControls;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004714 }
4715
4716 /**
4717 * Gets the {@link ZoomButtonsController} which can be used to add
4718 * additional buttons to the zoom controls window.
Cary Clarkd6982c92009-05-29 11:02:22 -04004719 *
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004720 * @return The instance of {@link ZoomButtonsController} used by this class,
4721 * or null if it is unavailable.
The Android Open Source Project10592532009-03-18 17:39:46 -07004722 * @hide
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004723 */
4724 public ZoomButtonsController getZoomButtonsController() {
4725 return mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004726 }
4727
4728 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004729 * Perform zoom in in the webview
4730 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
4731 */
4732 public boolean zoomIn() {
4733 // TODO: alternatively we can disallow this during draw history mode
4734 switchOutDrawHistory();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004735 // Center zooming to the center of the screen.
Grace Kloba455e3af2009-08-13 11:01:21 -07004736 if (mInZoomOverview) {
4737 // if in overview mode, bring it back to normal mode
4738 mLastTouchX = getViewWidth() * .5f;
4739 mLastTouchY = getViewHeight() * .5f;
4740 doDoubleTap();
4741 return true;
4742 } else {
4743 mZoomCenterX = getViewWidth() * .5f;
4744 mZoomCenterY = getViewHeight() * .5f;
4745 return zoomWithPreview(mActualScale * 1.25f);
4746 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004747 }
4748
4749 /**
4750 * Perform zoom out in the webview
4751 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
4752 */
4753 public boolean zoomOut() {
4754 // TODO: alternatively we can disallow this during draw history mode
4755 switchOutDrawHistory();
Grace Klobae397a882009-08-06 12:04:14 -07004756 float scale = mActualScale * 0.8f;
Grace Kloba04b28682009-09-14 14:38:37 -07004757 if (scale < (mMinZoomScale + 0.1f)
Grace Kloba86773fb2009-11-19 11:25:20 -08004758 && mWebViewCore.getSettings().getUseWideViewPort()
4759 && mZoomOverviewWidth > Math.ceil(getViewWidth()
4760 * mInvActualScale)) {
Grace Klobae397a882009-08-06 12:04:14 -07004761 // when zoom out to min scale, switch to overview mode
4762 doDoubleTap();
4763 return true;
4764 } else {
4765 // Center zooming to the center of the screen.
4766 mZoomCenterX = getViewWidth() * .5f;
4767 mZoomCenterY = getViewHeight() * .5f;
4768 return zoomWithPreview(scale);
4769 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004770 }
4771
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004772 private void updateSelection() {
4773 if (mNativeClass == 0) {
4774 return;
4775 }
4776 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04004777 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
4778 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004779 Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
4780 contentX + mNavSlop, contentY + mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004781 nativeSelectBestAt(rect);
4782 }
4783
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004784 /**
Leon Scroggins72543e12009-07-23 15:29:45 -04004785 * Scroll the focused text field/area to match the WebTextView
Cary Clarkeaa18de2009-09-28 12:50:42 -04004786 * @param xPercent New x position of the WebTextView from 0 to 1.
Leon Scroggins72543e12009-07-23 15:29:45 -04004787 * @param y New y position of the WebTextView in view coordinates
4788 */
Cary Clarkeaa18de2009-09-28 12:50:42 -04004789 /*package*/ void scrollFocusedTextInput(float xPercent, int y) {
Leon Scroggins72543e12009-07-23 15:29:45 -04004790 if (!inEditingMode() || mWebViewCore == null) {
4791 return;
4792 }
Cary Clarkeaa18de2009-09-28 12:50:42 -04004793 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT,
Leon Scrogginsd3997e52009-09-21 14:15:18 -04004794 // Since this position is relative to the top of the text input
4795 // field, we do not need to take the title bar's height into
4796 // consideration.
Cary Clarkeaa18de2009-09-28 12:50:42 -04004797 viewToContentDimension(y),
4798 new Float(xPercent));
Leon Scroggins72543e12009-07-23 15:29:45 -04004799 }
4800
4801 /**
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004802 * Set our starting point and time for a drag from the WebTextView.
4803 */
4804 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
4805 if (!inEditingMode()) {
4806 return;
4807 }
4808 mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
4809 mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
4810 mLastTouchTime = eventTime;
4811 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04004812 abortAnimation();
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004813 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
4814 }
4815 mSnapScrollMode = SNAP_NONE;
4816 mVelocityTracker = VelocityTracker.obtain();
4817 mTouchMode = TOUCH_DRAG_START_MODE;
4818 }
4819
4820 /**
4821 * Given a motion event from the WebTextView, set its location to our
4822 * coordinates, and handle the event.
4823 */
4824 /*package*/ boolean textFieldDrag(MotionEvent event) {
4825 if (!inEditingMode()) {
4826 return false;
4827 }
Leon Scroggins72543e12009-07-23 15:29:45 -04004828 mDragFromTextInput = true;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004829 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
4830 (float) (mWebTextView.getTop() - mScrollY));
Leon Scroggins72543e12009-07-23 15:29:45 -04004831 boolean result = onTouchEvent(event);
4832 mDragFromTextInput = false;
4833 return result;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004834 }
4835
Leon Scroggins6679f2f2009-08-12 18:48:10 -04004836 /**
Leon Scrogginsf90b1262009-11-24 14:49:21 -05004837 * Due a touch up from a WebTextView. This will be handled by webkit to
Leon Scroggins6679f2f2009-08-12 18:48:10 -04004838 * change the selection.
4839 * @param event MotionEvent in the WebTextView's coordinates.
4840 */
4841 /*package*/ void touchUpOnTextField(MotionEvent event) {
4842 if (!inEditingMode()) {
4843 return;
4844 }
Leon Scroggins0236e672009-09-02 21:12:08 -04004845 int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
4846 int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
Leon Scrogginsf90b1262009-11-24 14:49:21 -05004847 nativeMotionUp(x, y, mNavSlop);
Leon Scroggins6679f2f2009-08-12 18:48:10 -04004848 }
4849
Leon Scroggins1d96ca02009-10-23 11:49:03 -04004850 /**
4851 * Called when pressing the center key or trackball on a textfield.
4852 */
4853 /*package*/ void centerKeyPressOnTextField() {
4854 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
4855 nativeCursorNodePointer());
4856 // Need to show the soft keyboard if it's not readonly.
4857 if (!nativeCursorIsReadOnly()) {
4858 displaySoftKeyboard(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004859 }
4860 }
4861
4862 private void doShortPress() {
4863 if (mNativeClass == 0) {
4864 return;
4865 }
4866 switchOutDrawHistory();
4867 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04004868 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
4869 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
Cary Clark1cb97ee2009-12-11 12:10:36 -05004870 if (nativePointInNavCache(contentX, contentY, mNavSlop)) {
4871 WebViewCore.MotionUpData motionUpData = new WebViewCore
4872 .MotionUpData();
4873 motionUpData.mFrame = nativeCacheHitFramePointer();
4874 motionUpData.mNode = nativeCacheHitNodePointer();
4875 motionUpData.mBounds = nativeCacheHitNodeBounds();
4876 motionUpData.mX = contentX;
4877 motionUpData.mY = contentY;
4878 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
4879 motionUpData);
4880 } else {
4881 doMotionUp(contentX, contentY, false);
4882 }
4883 }
4884
4885 private void doMotionUp(int contentX, int contentY, boolean useNavCache) {
4886 if (nativeMotionUp(contentX, contentY, useNavCache ? mNavSlop : 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004887 if (mLogEvent) {
4888 Checkin.updateStats(mContext.getContentResolver(),
4889 Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
4890 }
4891 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004892 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004893 playSoundEffect(SoundEffectConstants.CLICK);
4894 }
4895 }
4896
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004897 private void doDoubleTap() {
Leon Scroggins0236e672009-09-02 21:12:08 -04004898 if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004899 return;
4900 }
4901 mZoomCenterX = mLastTouchX;
4902 mZoomCenterY = mLastTouchY;
4903 mInZoomOverview = !mInZoomOverview;
Grace Klobad7660cc2009-08-17 17:13:01 -07004904 // remove the zoom control after double tap
Grace Klobaf8d8b462009-09-20 15:57:49 -07004905 WebSettings settings = getSettings();
4906 if (settings.getBuiltInZoomControls()) {
Grace Klobad7660cc2009-08-17 17:13:01 -07004907 if (mZoomButtonsController.isVisible()) {
4908 mZoomButtonsController.setVisible(false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004909 }
Grace Klobad7660cc2009-08-17 17:13:01 -07004910 } else {
4911 if (mZoomControlRunnable != null) {
4912 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4913 }
4914 if (mZoomControls != null) {
4915 mZoomControls.hide();
4916 }
4917 }
Grace Klobaf8d8b462009-09-20 15:57:49 -07004918 settings.setDoubleTapToastCount(0);
Grace Klobad7660cc2009-08-17 17:13:01 -07004919 if (mInZoomOverview) {
Grace Kloba86773fb2009-11-19 11:25:20 -08004920 float newScale = (float) getViewWidth() / mZoomOverviewWidth;
4921 if (Math.abs(mActualScale - newScale) < 0.01f) {
4922 // reset mInZoomOverview to false if scale doesn't change
4923 mInZoomOverview = false;
4924 } else {
4925 // Force the titlebar fully reveal in overview mode
4926 if (mScrollY < getTitleHeight()) mScrollY = 0;
4927 zoomWithPreview(newScale);
4928 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004929 } else {
4930 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04004931 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
4932 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
Cary Clark77d98f42009-07-31 09:40:38 -04004933 int left = nativeGetBlockLeftEdge(contentX, contentY, mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004934 if (left != NO_LEFTEDGE) {
4935 // add a 5pt padding to the left edge. Re-calculate the zoom
4936 // center so that the new scroll x will be on the left edge.
4937 mZoomCenterX = left < 5 ? 0 : (left - 5) * mLastScale
4938 * mActualScale / (mLastScale - mActualScale);
4939 }
4940 zoomWithPreview(mLastScale);
4941 }
4942 }
4943
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004944 // Called by JNI to handle a touch on a node representing an email address,
4945 // address, or phone number
4946 private void overrideLoading(String url) {
4947 mCallbackProxy.uiOverrideUrlLoading(url);
4948 }
4949
4950 @Override
4951 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
4952 boolean result = false;
4953 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004954 result = mWebTextView.requestFocus(direction,
4955 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004956 } else {
4957 result = super.requestFocus(direction, previouslyFocusedRect);
4958 if (mWebViewCore.getSettings().getNeedInitialFocus()) {
4959 // For cases such as GMail, where we gain focus from a direction,
4960 // we want to move to the first available link.
4961 // FIXME: If there are no visible links, we may not want to
4962 int fakeKeyDirection = 0;
4963 switch(direction) {
4964 case View.FOCUS_UP:
4965 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
4966 break;
4967 case View.FOCUS_DOWN:
4968 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
4969 break;
4970 case View.FOCUS_LEFT:
4971 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
4972 break;
4973 case View.FOCUS_RIGHT:
4974 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
4975 break;
4976 default:
4977 return result;
4978 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004979 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Cary Clark215b72c2009-06-26 14:38:43 -04004980 navHandledKey(fakeKeyDirection, 1, true, 0, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004981 }
4982 }
4983 }
4984 return result;
4985 }
4986
4987 @Override
4988 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
4989 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4990
4991 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
4992 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
4993 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
4994 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
4995
4996 int measuredHeight = heightSize;
4997 int measuredWidth = widthSize;
4998
4999 // Grab the content size from WebViewCore.
Grace Klobae621d6f2009-09-11 13:20:39 -07005000 int contentHeight = contentToViewDimension(mContentHeight);
5001 int contentWidth = contentToViewDimension(mContentWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005002
5003// Log.d(LOGTAG, "------- measure " + heightMode);
5004
5005 if (heightMode != MeasureSpec.EXACTLY) {
5006 mHeightCanMeasure = true;
5007 measuredHeight = contentHeight;
5008 if (heightMode == MeasureSpec.AT_MOST) {
5009 // If we are larger than the AT_MOST height, then our height can
5010 // no longer be measured and we should scroll internally.
5011 if (measuredHeight > heightSize) {
5012 measuredHeight = heightSize;
5013 mHeightCanMeasure = false;
5014 }
5015 }
5016 } else {
5017 mHeightCanMeasure = false;
5018 }
5019 if (mNativeClass != 0) {
5020 nativeSetHeightCanMeasure(mHeightCanMeasure);
5021 }
5022 // For the width, always use the given size unless unspecified.
5023 if (widthMode == MeasureSpec.UNSPECIFIED) {
5024 mWidthCanMeasure = true;
5025 measuredWidth = contentWidth;
5026 } else {
5027 mWidthCanMeasure = false;
5028 }
5029
5030 synchronized (this) {
5031 setMeasuredDimension(measuredWidth, measuredHeight);
5032 }
5033 }
5034
5035 @Override
5036 public boolean requestChildRectangleOnScreen(View child,
5037 Rect rect,
5038 boolean immediate) {
5039 rect.offset(child.getLeft() - child.getScrollX(),
5040 child.getTop() - child.getScrollY());
5041
Grace Kloba8eff73f2009-09-24 09:34:32 -07005042 int height = getViewHeightWithTitle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005043 int screenTop = mScrollY;
5044 int screenBottom = screenTop + height;
5045
5046 int scrollYDelta = 0;
5047
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005048 if (rect.bottom > screenBottom) {
5049 int oneThirdOfScreenHeight = height / 3;
5050 if (rect.height() > 2 * oneThirdOfScreenHeight) {
5051 // If the rectangle is too tall to fit in the bottom two thirds
5052 // of the screen, place it at the top.
5053 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005054 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005055 // If the rectangle will still fit on screen, we want its
5056 // top to be in the top third of the screen.
5057 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005058 }
5059 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005060 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005061 }
5062
5063 int width = getWidth() - getVerticalScrollbarWidth();
5064 int screenLeft = mScrollX;
5065 int screenRight = screenLeft + width;
5066
5067 int scrollXDelta = 0;
5068
5069 if (rect.right > screenRight && rect.left > screenLeft) {
5070 if (rect.width() > width) {
5071 scrollXDelta += (rect.left - screenLeft);
5072 } else {
5073 scrollXDelta += (rect.right - screenRight);
5074 }
5075 } else if (rect.left < screenLeft) {
5076 scrollXDelta -= (screenLeft - rect.left);
5077 }
5078
5079 if ((scrollYDelta | scrollXDelta) != 0) {
5080 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
5081 }
5082
5083 return false;
5084 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005085
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005086 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
5087 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04005088 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
5089 arg.mReplace = replace;
5090 arg.mNewStart = newStart;
5091 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07005092 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04005093 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005094 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
5095 }
5096
5097 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkded054c2009-06-15 10:26:08 -04005098 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
5099 arg.mEvent = event;
5100 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005101 // Increase our text generation number, and pass it to webcore thread
5102 mTextGeneration++;
5103 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
5104 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04005105 // To make sure the host application, like Browser, has the up to date
5106 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005107 // document state.
5108 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
5109 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04005110 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005111 }
5112
5113 /* package */ WebViewCore getWebViewCore() {
5114 return mWebViewCore;
5115 }
5116
5117 //-------------------------------------------------------------------------
5118 // Methods can be called from a separate thread, like WebViewCore
5119 // If it needs to call the View system, it has to send message.
5120 //-------------------------------------------------------------------------
5121
5122 /**
5123 * General handler to receive message coming from webkit thread
5124 */
5125 class PrivateHandler extends Handler {
5126 @Override
5127 public void handleMessage(Message msg) {
Cary Clark3e88ddc2009-10-08 14:59:46 -04005128 // exclude INVAL_RECT_MSG_ID since it is frequently output
5129 if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005130 Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
Grace Kloba11438c32009-12-16 11:39:12 -08005131 > HIDE_FULLSCREEN ? Integer.toString(msg.what)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005132 : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
5133 }
Grace Kloba207308a2009-09-27 11:44:14 -07005134 if (mWebViewCore == null) {
5135 // after WebView's destroy() is called, skip handling messages.
5136 return;
5137 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005138 switch (msg.what) {
5139 case REMEMBER_PASSWORD: {
5140 mDatabase.setUsernamePassword(
5141 msg.getData().getString("host"),
5142 msg.getData().getString("username"),
5143 msg.getData().getString("password"));
5144 ((Message) msg.obj).sendToTarget();
5145 break;
5146 }
5147 case NEVER_REMEMBER_PASSWORD: {
5148 mDatabase.setUsernamePassword(
5149 msg.getData().getString("host"), null, null);
5150 ((Message) msg.obj).sendToTarget();
5151 break;
5152 }
5153 case SWITCH_TO_SHORTPRESS: {
Grace Klobaf58af622009-09-24 17:41:23 -07005154 // if mPreventDrag is not confirmed, treat it as no so that
5155 // it won't block panning the page.
5156 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
5157 mPreventDrag = PREVENT_DRAG_NO;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005158 mPreventLongPress = false;
5159 mPreventDoubleTap = false;
Grace Klobaf58af622009-09-24 17:41:23 -07005160 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005161 if (mTouchMode == TOUCH_INIT_MODE) {
Grace Kloba11438c32009-12-16 11:39:12 -08005162 mTouchMode = mFullScreenHolder == null
5163 ? TOUCH_SHORTPRESS_START_MODE
5164 : TOUCH_SHORTPRESS_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005165 updateSelection();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005166 } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
5167 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005168 }
5169 break;
5170 }
5171 case SWITCH_TO_LONGPRESS: {
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005172 if (mPreventLongPress) {
5173 mTouchMode = TOUCH_DONE_MODE;
5174 WebViewCore.TouchEventData ted
5175 = new WebViewCore.TouchEventData();
5176 ted.mAction = WebViewCore.ACTION_LONGPRESS;
5177 ted.mX = viewToContentX((int) mLastTouchX + mScrollX);
5178 ted.mY = viewToContentY((int) mLastTouchY + mScrollY);
5179 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
5180 } else if (mPreventDrag == PREVENT_DRAG_NO) {
Grace Kloba6c451b72009-06-25 12:25:30 -07005181 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba11438c32009-12-16 11:39:12 -08005182 if (mFullScreenHolder == null) {
5183 performLongClick();
5184 rebuildWebTextView();
5185 }
Grace Kloba6c451b72009-06-25 12:25:30 -07005186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005187 break;
5188 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005189 case RELEASE_SINGLE_TAP: {
Grace Klobaf58af622009-09-24 17:41:23 -07005190 if (mPreventDrag == PREVENT_DRAG_NO) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005191 mTouchMode = TOUCH_DONE_MODE;
5192 doShortPress();
5193 }
5194 break;
5195 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005196 case SCROLL_BY_MSG_ID:
5197 setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
5198 break;
5199 case SYNC_SCROLL_TO_MSG_ID:
5200 if (mUserScroll) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005201 // if user has scrolled explicitly, don't sync the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005202 // scroll position any more
5203 mUserScroll = false;
5204 break;
5205 }
5206 // fall through
5207 case SCROLL_TO_MSG_ID:
5208 if (setContentScrollTo(msg.arg1, msg.arg2)) {
5209 // if we can't scroll to the exact position due to pin,
Cary Clarkd6982c92009-05-29 11:02:22 -04005210 // send a message to WebCore to re-scroll when we get a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005211 // new picture
5212 mUserScroll = false;
5213 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
5214 msg.arg1, msg.arg2);
5215 }
5216 break;
5217 case SPAWN_SCROLL_TO_MSG_ID:
5218 spawnContentScrollTo(msg.arg1, msg.arg2);
5219 break;
Grace Klobae397a882009-08-06 12:04:14 -07005220 case NEW_PICTURE_MSG_ID: {
5221 WebSettings settings = mWebViewCore.getSettings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005222 // called for new content
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005223 final int viewWidth = getViewWidth();
Cary Clarkd6982c92009-05-29 11:02:22 -04005224 final WebViewCore.DrawData draw =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005225 (WebViewCore.DrawData) msg.obj;
5226 final Point viewSize = draw.mViewPoint;
Grace Klobae397a882009-08-06 12:04:14 -07005227 boolean useWideViewport = settings.getUseWideViewPort();
Grace Klobaef347ef2009-07-30 11:20:32 -07005228 WebViewCore.RestoreState restoreState = draw.mRestoreState;
5229 if (restoreState != null) {
5230 mInZoomOverview = false;
Grace Kloba16efce72009-11-10 15:49:03 -08005231 mLastScale = mInitialScaleInPercent > 0
5232 ? mInitialScaleInPercent / 100.0f
5233 : restoreState.mTextWrapScale;
Grace Klobaef347ef2009-07-30 11:20:32 -07005234 if (restoreState.mMinScale == 0) {
Grace Kloba3c72fff2009-09-04 13:15:59 -07005235 if (restoreState.mMobileSite) {
Cary Clarkb82665e2009-09-23 12:49:28 -04005236 if (draw.mMinPrefWidth >
5237 Math.max(0, draw.mViewPoint.x)) {
Grace Kloba3c72fff2009-09-04 13:15:59 -07005238 mMinZoomScale = (float) viewWidth
5239 / draw.mMinPrefWidth;
5240 mMinZoomScaleFixed = false;
5241 } else {
Grace Kloba408cf852009-09-20 16:34:44 -07005242 mMinZoomScale = restoreState.mDefaultScale;
Grace Kloba3c72fff2009-09-04 13:15:59 -07005243 mMinZoomScaleFixed = true;
5244 }
5245 } else {
5246 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
5247 mMinZoomScaleFixed = false;
5248 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005249 } else {
5250 mMinZoomScale = restoreState.mMinScale;
5251 mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005252 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005253 if (restoreState.mMaxScale == 0) {
5254 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
5255 } else {
5256 mMaxZoomScale = restoreState.mMaxScale;
5257 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005258 setNewZoomScale(mLastScale, false);
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04005259 setContentScrollTo(restoreState.mScrollX,
5260 restoreState.mScrollY);
Grace Kloba04b28682009-09-14 14:38:37 -07005261 if (useWideViewport
Grace Kloba3c72fff2009-09-04 13:15:59 -07005262 && settings.getLoadWithOverviewMode()) {
5263 if (restoreState.mViewScale == 0
5264 || (restoreState.mMobileSite
Grace Kloba408cf852009-09-20 16:34:44 -07005265 && mMinZoomScale < restoreState.mDefaultScale)) {
Leon Scrogginsb2359262009-08-19 16:19:26 -04005266 mInZoomOverview = true;
Leon Scrogginsb2359262009-08-19 16:19:26 -04005267 }
5268 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005269 // As we are on a new page, remove the WebTextView. This
5270 // is necessary for page loads driven by webkit, and in
5271 // particular when the user was on a password field, so
5272 // the WebTextView was visible.
5273 clearTextEntry();
Mike Reed8b302092009-11-12 12:50:20 -05005274 // update the zoom buttons as the scale can be changed
5275 if (getSettings().getBuiltInZoomControls()) {
5276 updateZoomButtonsEnabled();
5277 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005278 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005279 // We update the layout (i.e. request a layout from the
5280 // view system) if the last view size that we sent to
5281 // WebCore matches the view size of the picture we just
5282 // received in the fixed dimension.
5283 final boolean updateLayout = viewSize.x == mLastWidthSent
5284 && viewSize.y == mLastHeightSent;
Cary Clarkd6982c92009-05-29 11:02:22 -04005285 recordNewContentSize(draw.mWidthHeight.x,
Cary Clark5bb6b522009-09-21 11:58:31 -04005286 draw.mWidthHeight.y
5287 + (mFindIsUp ? mFindHeight : 0), updateLayout);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005288 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005289 Rect b = draw.mInvalRegion.getBounds();
5290 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
5291 b.left+","+b.top+","+b.right+","+b.bottom+"}");
5292 }
Mike Reede9e86b82009-09-15 11:26:53 -04005293 invalidateContentRect(draw.mInvalRegion.getBounds());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005294 if (mPictureListener != null) {
5295 mPictureListener.onNewPicture(WebView.this, capturePicture());
5296 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005297 if (useWideViewport) {
Grace Klobaa4fa1072009-11-09 12:01:50 -08005298 // limit mZoomOverviewWidth to sMaxViewportWidth so that
5299 // if the page doesn't behave well, the WebView won't go
5300 // insane.
5301 mZoomOverviewWidth = Math.min(sMaxViewportWidth, Math
5302 .max(draw.mMinPrefWidth, draw.mViewPoint.x));
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005303 }
5304 if (!mMinZoomScaleFixed) {
Grace Klobae397a882009-08-06 12:04:14 -07005305 mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005306 }
5307 if (!mDrawHistory && mInZoomOverview) {
5308 // fit the content width to the current view. Ignore
5309 // the rounding error case.
5310 if (Math.abs((viewWidth * mInvActualScale)
5311 - mZoomOverviewWidth) > 1) {
Grace Klobae397a882009-08-06 12:04:14 -07005312 setNewZoomScale((float) viewWidth
5313 / mZoomOverviewWidth, false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005314 }
5315 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04005316 if (draw.mFocusSizeChanged && inEditingMode()) {
5317 mFocusSizeChanged = true;
5318 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005319 break;
Grace Klobae397a882009-08-06 12:04:14 -07005320 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005321 case WEBCORE_INITIALIZED_MSG_ID:
5322 // nativeCreate sets mNativeClass to a non-zero value
5323 nativeCreate(msg.arg1);
5324 break;
5325 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
5326 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04005327 // and representing the same node as the pointer.
5328 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005329 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005330 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005331 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005332 int start = Selection.getSelectionStart(text);
5333 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005334 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005335 // Restore the selection, which may have been
5336 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005337 Spannable pword =
5338 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005339 Selection.setSelection(pword, start, end);
5340 // If the text entry has created more events, ignore
5341 // this one.
5342 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005343 mWebTextView.setTextAndKeepSelection(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005344 (String) msg.obj);
5345 }
5346 }
5347 break;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005348 case UPDATE_TEXT_SELECTION_MSG_ID:
5349 if (inEditingMode()
5350 && mWebTextView.isSameTextField(msg.arg1)
5351 && msg.arg2 == mTextGeneration) {
5352 WebViewCore.TextSelectionData tData
5353 = (WebViewCore.TextSelectionData) msg.obj;
5354 mWebTextView.setSelectionFromWebKit(tData.mStart,
5355 tData.mEnd);
5356 }
5357 break;
Cary Clark215b72c2009-06-26 14:38:43 -04005358 case MOVE_OUT_OF_PLUGIN:
Derek Sollenberger718d69f2009-10-19 15:56:43 -04005359 navHandledKey(msg.arg1, 1, false, 0, true);
Cary Clark215b72c2009-06-26 14:38:43 -04005360 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005361 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04005362 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005363 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04005364 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005365 mWebTextView.bringIntoView();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005366 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005367 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005368 break;
Cary Clark243ea062009-06-25 10:49:32 -04005369 case CLEAR_TEXT_ENTRY:
5370 clearTextEntry();
5371 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005372 case INVAL_RECT_MSG_ID: {
5373 Rect r = (Rect)msg.obj;
5374 if (r == null) {
5375 invalidate();
5376 } else {
5377 // we need to scale r from content into view coords,
5378 // which viewInvalidate() does for us
5379 viewInvalidate(r.left, r.top, r.right, r.bottom);
5380 }
5381 break;
5382 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005383 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04005384 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005385 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005386 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005387 }
5388 break;
5389 case UPDATE_CLIPBOARD:
5390 String str = (String) msg.obj;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005391 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005392 Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
5393 }
5394 try {
5395 IClipboard clip = IClipboard.Stub.asInterface(
5396 ServiceManager.getService("clipboard"));
5397 clip.setClipboardText(str);
5398 } catch (android.os.RemoteException e) {
5399 Log.e(LOGTAG, "Clipboard failed", e);
5400 }
5401 break;
5402 case RESUME_WEBCORE_UPDATE:
5403 WebViewCore.resumeUpdate(mWebViewCore);
5404 break;
5405
Leon Scrogginse3225672009-06-03 15:53:13 -04005406 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005407 // as this is shared by keydown and trackballdown, reset all
5408 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04005409 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005410 mTrackballDown = false;
Leon Scrogginse3225672009-06-03 15:53:13 -04005411 // LONG_PRESS_CENTER is sent as a delayed message. If we
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005412 // switch to windows overview, the WebView will be
5413 // temporarily removed from the view system. In that case,
5414 // do nothing.
5415 if (getParent() != null) {
5416 performLongClick();
5417 }
5418 break;
5419
5420 case WEBCORE_NEED_TOUCH_EVENTS:
5421 mForwardTouchEvents = (msg.arg1 != 0);
5422 break;
5423
5424 case PREVENT_TOUCH_ID:
5425 if (msg.arg1 == MotionEvent.ACTION_DOWN) {
Grace Klobaf58af622009-09-24 17:41:23 -07005426 // dont override if mPreventDrag has been set to no due
5427 // to time out
5428 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005429 mPreventDrag = (msg.arg2 & TOUCH_PREVENT_DRAG)
5430 == TOUCH_PREVENT_DRAG ? PREVENT_DRAG_YES
Grace Klobaf58af622009-09-24 17:41:23 -07005431 : PREVENT_DRAG_NO;
5432 if (mPreventDrag == PREVENT_DRAG_YES) {
5433 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005434 } else {
5435 mPreventLongPress =
5436 (msg.arg2 & TOUCH_PREVENT_LONGPRESS)
5437 == TOUCH_PREVENT_LONGPRESS;
5438 mPreventDoubleTap =
5439 (msg.arg2 & TOUCH_PREVENT_DOUBLETAP)
5440 == TOUCH_PREVENT_DOUBLETAP;
Grace Klobaf58af622009-09-24 17:41:23 -07005441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005442 }
5443 }
5444 break;
5445
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005446 case REQUEST_KEYBOARD:
5447 if (msg.arg1 == 0) {
5448 hideSoftKeyboard();
5449 } else {
5450 displaySoftKeyboard(false);
Cary Clark3e88ddc2009-10-08 14:59:46 -04005451 if (DebugFlags.WEB_VIEW) {
5452 Log.v(LOGTAG, "REQUEST_KEYBOARD"
5453 + " focusCandidateIsPlugin="
5454 + nativeFocusCandidateIsPlugin());
5455 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005456 }
5457 break;
5458
Leon Scroggins5de63892009-10-29 09:48:43 -04005459 case FIND_AGAIN:
5460 // Ignore if find has been dismissed.
5461 if (mFindIsUp) {
5462 findAll(mLastFind);
5463 }
5464 break;
5465
Cary Clark25415e22009-10-12 13:41:28 -04005466 case DRAG_HELD_MOTIONLESS:
5467 mHeldMotionless = MOTIONLESS_TRUE;
5468 invalidate();
5469 // fall through to keep scrollbars awake
5470
5471 case AWAKEN_SCROLL_BARS:
5472 if (mTouchMode == TOUCH_DRAG_MODE
5473 && mHeldMotionless == MOTIONLESS_TRUE) {
5474 awakenScrollBars(ViewConfiguration
5475 .getScrollDefaultDelay(), false);
5476 mPrivateHandler.sendMessageDelayed(mPrivateHandler
5477 .obtainMessage(AWAKEN_SCROLL_BARS),
5478 ViewConfiguration.getScrollDefaultDelay());
5479 }
5480 break;
Cary Clark1cb97ee2009-12-11 12:10:36 -05005481
5482 case DO_MOTION_UP:
5483 doMotionUp(msg.arg1, msg.arg2, (Boolean) msg.obj);
5484 break;
5485
Grace Kloba11438c32009-12-16 11:39:12 -08005486 case SHOW_FULLSCREEN:
5487 WebViewCore.PluginFullScreenData data
5488 = (WebViewCore.PluginFullScreenData) msg.obj;
5489 if (data.mNpp != 0 && data.mView != null) {
5490 if (mFullScreenHolder != null) {
5491 Log.w(LOGTAG,
5492 "Should not have another full screen.");
5493 mFullScreenHolder.dismiss();
5494 }
5495 mFullScreenHolder = new PluginFullScreenHolder(
5496 WebView.this, data.mNpp);
5497 mFullScreenHolder.setContentView(data.mView);
5498 mFullScreenHolder.setCancelable(false);
5499 mFullScreenHolder.setCanceledOnTouchOutside(false);
5500 }
5501 mFullScreenHolder.updateBound(contentToViewX(data.mDocX)
5502 - mScrollX, contentToViewY(data.mDocY) - mScrollY,
5503 contentToViewDimension(data.mDocWidth),
5504 contentToViewDimension(data.mDocHeight));
5505 mFullScreenHolder.show();
5506 break;
5507
5508 case HIDE_FULLSCREEN:
5509 if (mFullScreenHolder != null) {
5510 mFullScreenHolder.dismiss();
5511 mFullScreenHolder = null;
5512 }
5513 break;
5514
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005515 default:
5516 super.handleMessage(msg);
5517 break;
5518 }
5519 }
5520 }
5521
5522 // Class used to use a dropdown for a <select> element
5523 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005524 // Whether the listbox allows multiple selection.
5525 private boolean mMultiple;
5526 // Passed in to a list with multiple selection to tell
5527 // which items are selected.
5528 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04005529 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005530 // where the initial selection is.
5531 private int mSelection;
5532
5533 private Container[] mContainers;
5534
5535 // Need these to provide stable ids to my ArrayAdapter,
5536 // which normally does not have stable ids. (Bug 1250098)
5537 private class Container extends Object {
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005538 /**
5539 * Possible values for mEnabled. Keep in sync with OptionStatus in
5540 * WebViewCore.cpp
5541 */
5542 final static int OPTGROUP = -1;
5543 final static int OPTION_DISABLED = 0;
5544 final static int OPTION_ENABLED = 1;
5545
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005546 String mString;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005547 int mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005548 int mId;
5549
5550 public String toString() {
5551 return mString;
5552 }
5553 }
5554
5555 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04005556 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005557 * and allow filtering.
5558 */
5559 private class MyArrayListAdapter extends ArrayAdapter<Container> {
5560 public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005561 super(context,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005562 multiple ? com.android.internal.R.layout.select_dialog_multichoice :
Cary Clarkd6982c92009-05-29 11:02:22 -04005563 com.android.internal.R.layout.select_dialog_singlechoice,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005564 objects);
5565 }
5566
5567 @Override
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005568 public View getView(int position, View convertView,
5569 ViewGroup parent) {
5570 // Always pass in null so that we will get a new CheckedTextView
5571 // Otherwise, an item which was previously used as an <optgroup>
5572 // element (i.e. has no check), could get used as an <option>
5573 // element, which needs a checkbox/radio, but it would not have
5574 // one.
5575 convertView = super.getView(position, null, parent);
5576 Container c = item(position);
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -04005577 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
5578 // ListView does not draw dividers between disabled and
5579 // enabled elements. Use a LinearLayout to provide dividers
5580 LinearLayout layout = new LinearLayout(mContext);
5581 layout.setOrientation(LinearLayout.VERTICAL);
5582 if (position > 0) {
5583 View dividerTop = new View(mContext);
5584 dividerTop.setBackgroundResource(
5585 android.R.drawable.divider_horizontal_bright);
5586 layout.addView(dividerTop);
5587 }
5588
5589 if (Container.OPTGROUP == c.mEnabled) {
5590 // Currently select_dialog_multichoice and
5591 // select_dialog_singlechoice are CheckedTextViews. If
5592 // that changes, the class cast will no longer be valid.
5593 Assert.assertTrue(
5594 convertView instanceof CheckedTextView);
5595 ((CheckedTextView) convertView).setCheckMarkDrawable(
5596 null);
5597 } else {
5598 // c.mEnabled == Container.OPTION_DISABLED
5599 // Draw the disabled element in a disabled state.
5600 convertView.setEnabled(false);
5601 }
5602
5603 layout.addView(convertView);
5604 if (position < getCount() - 1) {
5605 View dividerBottom = new View(mContext);
5606 dividerBottom.setBackgroundResource(
5607 android.R.drawable.divider_horizontal_bright);
5608 layout.addView(dividerBottom);
5609 }
5610 return layout;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005611 }
5612 return convertView;
5613 }
5614
5615 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005616 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04005617 // AdapterView's onChanged method uses this to determine whether
5618 // to restore the old state. Return false so that the old (out
5619 // of date) state does not replace the new, valid state.
5620 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005621 }
5622
5623 private Container item(int position) {
5624 if (position < 0 || position >= getCount()) {
5625 return null;
5626 }
5627 return (Container) getItem(position);
5628 }
5629
5630 @Override
5631 public long getItemId(int position) {
5632 Container item = item(position);
5633 if (item == null) {
5634 return -1;
5635 }
5636 return item.mId;
5637 }
5638
5639 @Override
5640 public boolean areAllItemsEnabled() {
5641 return false;
5642 }
5643
5644 @Override
5645 public boolean isEnabled(int position) {
5646 Container item = item(position);
5647 if (item == null) {
5648 return false;
5649 }
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005650 return Container.OPTION_ENABLED == item.mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005651 }
5652 }
5653
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005654 private InvokeListBox(String[] array, int[] enabled, int[] selected) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005655 mMultiple = true;
5656 mSelectedArray = selected;
5657
5658 int length = array.length;
5659 mContainers = new Container[length];
5660 for (int i = 0; i < length; i++) {
5661 mContainers[i] = new Container();
5662 mContainers[i].mString = array[i];
5663 mContainers[i].mEnabled = enabled[i];
5664 mContainers[i].mId = i;
5665 }
5666 }
5667
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005668 private InvokeListBox(String[] array, int[] enabled, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005669 mSelection = selection;
5670 mMultiple = false;
5671
5672 int length = array.length;
5673 mContainers = new Container[length];
5674 for (int i = 0; i < length; i++) {
5675 mContainers[i] = new Container();
5676 mContainers[i].mString = array[i];
5677 mContainers[i].mEnabled = enabled[i];
5678 mContainers[i].mId = i;
5679 }
5680 }
5681
Leon Scroggins3667ce42009-05-13 15:58:03 -04005682 /*
5683 * Whenever the data set changes due to filtering, this class ensures
5684 * that the checked item remains checked.
5685 */
5686 private class SingleDataSetObserver extends DataSetObserver {
5687 private long mCheckedId;
5688 private ListView mListView;
5689 private Adapter mAdapter;
5690
5691 /*
5692 * Create a new observer.
5693 * @param id The ID of the item to keep checked.
5694 * @param l ListView for getting and clearing the checked states
5695 * @param a Adapter for getting the IDs
5696 */
5697 public SingleDataSetObserver(long id, ListView l, Adapter a) {
5698 mCheckedId = id;
5699 mListView = l;
5700 mAdapter = a;
5701 }
5702
5703 public void onChanged() {
5704 // The filter may have changed which item is checked. Find the
5705 // item that the ListView thinks is checked.
5706 int position = mListView.getCheckedItemPosition();
5707 long id = mAdapter.getItemId(position);
5708 if (mCheckedId != id) {
5709 // Clear the ListView's idea of the checked item, since
5710 // it is incorrect
5711 mListView.clearChoices();
5712 // Search for mCheckedId. If it is in the filtered list,
5713 // mark it as checked
5714 int count = mAdapter.getCount();
5715 for (int i = 0; i < count; i++) {
5716 if (mAdapter.getItemId(i) == mCheckedId) {
5717 mListView.setItemChecked(i, true);
5718 break;
5719 }
5720 }
5721 }
5722 }
5723
5724 public void onInvalidate() {}
5725 }
5726
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005727 public void run() {
5728 final ListView listView = (ListView) LayoutInflater.from(mContext)
5729 .inflate(com.android.internal.R.layout.select_dialog, null);
Cary Clarkd6982c92009-05-29 11:02:22 -04005730 final MyArrayListAdapter adapter = new
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005731 MyArrayListAdapter(mContext, mContainers, mMultiple);
5732 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
5733 .setView(listView).setCancelable(true)
5734 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04005735
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005736 if (mMultiple) {
5737 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
5738 public void onClick(DialogInterface dialog, int which) {
5739 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04005740 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005741 adapter.getCount(), 0,
5742 listView.getCheckedItemPositions());
5743 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07005744 b.setNegativeButton(android.R.string.cancel,
5745 new DialogInterface.OnClickListener() {
5746 public void onClick(DialogInterface dialog, int which) {
5747 mWebViewCore.sendMessage(
5748 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5749 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005750 }
5751 final AlertDialog dialog = b.create();
5752 listView.setAdapter(adapter);
5753 listView.setFocusableInTouchMode(true);
5754 // There is a bug (1250103) where the checks in a ListView with
5755 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04005756 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005757 // filtered. Do not allow filtering on multiple lists until
5758 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04005759
Leon Scroggins3667ce42009-05-13 15:58:03 -04005760 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005761 if (mMultiple) {
5762 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
5763 int length = mSelectedArray.length;
5764 for (int i = 0; i < length; i++) {
5765 listView.setItemChecked(mSelectedArray[i], true);
5766 }
5767 } else {
5768 listView.setOnItemClickListener(new OnItemClickListener() {
5769 public void onItemClick(AdapterView parent, View v,
5770 int position, long id) {
5771 mWebViewCore.sendMessage(
5772 EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
5773 dialog.dismiss();
5774 }
5775 });
5776 if (mSelection != -1) {
5777 listView.setSelection(mSelection);
5778 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
5779 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04005780 DataSetObserver observer = new SingleDataSetObserver(
5781 adapter.getItemId(mSelection), listView, adapter);
5782 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005783 }
5784 }
5785 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
5786 public void onCancel(DialogInterface dialog) {
5787 mWebViewCore.sendMessage(
5788 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5789 }
5790 });
5791 dialog.show();
5792 }
5793 }
5794
5795 /*
5796 * Request a dropdown menu for a listbox with multiple selection.
5797 *
5798 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005799 * @param enabledArray State for each element in the list. See static
5800 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005801 * @param selectedArray Which positions are initally selected.
5802 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005803 void requestListBox(String[] array, int[] enabledArray, int[]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005804 selectedArray) {
5805 mPrivateHandler.post(
5806 new InvokeListBox(array, enabledArray, selectedArray));
5807 }
5808
5809 /*
5810 * Request a dropdown menu for a listbox with single selection or a single
5811 * <select> element.
5812 *
5813 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005814 * @param enabledArray State for each element in the list. See static
5815 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005816 * @param selection Which position is initally selected.
5817 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04005818 void requestListBox(String[] array, int[] enabledArray, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005819 mPrivateHandler.post(
5820 new InvokeListBox(array, enabledArray, selection));
5821 }
5822
5823 // called by JNI
Leon Scroggins47fabbf2009-12-08 16:57:26 -05005824 private void sendMoveFocus(int frame, int node) {
5825 mWebViewCore.sendMessage(EventHub.SET_MOVE_FOCUS,
5826 new WebViewCore.CursorData(frame, node, 0, 0));
5827 }
5828
5829 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005830 private void sendMoveMouse(int frame, int node, int x, int y) {
5831 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
5832 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005833 }
5834
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005835 /*
5836 * Send a mouse move event to the webcore thread.
5837 *
5838 * @param removeFocus Pass true if the "mouse" cursor is now over a node
5839 * which wants key events, but it is not the focus. This
5840 * will make the visual appear as though nothing is in
5841 * focus. Remove the WebTextView, if present, and stop
5842 * drawing the blinking caret.
5843 * called by JNI
5844 */
5845 private void sendMoveMouseIfLatest(boolean removeFocus) {
5846 if (removeFocus) {
5847 clearTextEntry();
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04005848 setFocusControllerInactive();
Cary Clark19436562009-06-04 16:25:07 -04005849 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005850 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
5851 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005852 }
5853
5854 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005855 private void sendMotionUp(int touchGeneration,
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005856 int frame, int node, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005857 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
5858 touchUpData.mMoveGeneration = touchGeneration;
Cary Clarkd6982c92009-05-29 11:02:22 -04005859 touchUpData.mFrame = frame;
5860 touchUpData.mNode = node;
5861 touchUpData.mX = x;
5862 touchUpData.mY = y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005863 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
5864 }
5865
5866
5867 private int getScaledMaxXScroll() {
5868 int width;
5869 if (mHeightCanMeasure == false) {
5870 width = getViewWidth() / 4;
5871 } else {
5872 Rect visRect = new Rect();
5873 calcOurVisibleRect(visRect);
5874 width = visRect.width() / 2;
5875 }
5876 // FIXME the divisor should be retrieved from somewhere
Leon Scroggins0236e672009-09-02 21:12:08 -04005877 return viewToContentX(width);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005878 }
5879
5880 private int getScaledMaxYScroll() {
5881 int height;
5882 if (mHeightCanMeasure == false) {
5883 height = getViewHeight() / 4;
5884 } else {
5885 Rect visRect = new Rect();
5886 calcOurVisibleRect(visRect);
5887 height = visRect.height() / 2;
5888 }
5889 // FIXME the divisor should be retrieved from somewhere
5890 // the closest thing today is hard-coded into ScrollView.java
5891 // (from ScrollView.java, line 363) int maxJump = height/2;
Cary Clarkdf344372009-09-15 12:47:39 -04005892 return Math.round(height * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005893 }
5894
5895 /**
5896 * Called by JNI to invalidate view
5897 */
5898 private void viewInvalidate() {
5899 invalidate();
5900 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005901
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005902 // return true if the key was handled
Cary Clark215b72c2009-06-26 14:38:43 -04005903 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
5904 long time, boolean ignorePlugin) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005905 if (mNativeClass == 0) {
5906 return false;
5907 }
Derek Sollenberger718d69f2009-10-19 15:56:43 -04005908 if (ignorePlugin == false && nativeFocusIsPlugin()) {
Cary Clark215b72c2009-06-26 14:38:43 -04005909 KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
5910 , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
5911 | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
5912 | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
5913 , 0, 0, 0);
5914 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
5915 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
5916 return true;
5917 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005918 mLastCursorTime = time;
5919 mLastCursorBounds = nativeGetCursorRingBounds();
5920 boolean keyHandled
5921 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005922 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005923 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
5924 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005925 + " handled=" + keyHandled);
5926 }
5927 if (keyHandled == false || mHeightCanMeasure == false) {
5928 return keyHandled;
5929 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005930 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
5931 if (contentCursorRingBounds.isEmpty()) return keyHandled;
Mike Reede9e86b82009-09-15 11:26:53 -04005932 Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005933 Rect visRect = new Rect();
5934 calcOurVisibleRect(visRect);
5935 Rect outset = new Rect(visRect);
5936 int maxXScroll = visRect.width() / 2;
5937 int maxYScroll = visRect.height() / 2;
5938 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005939 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005940 return keyHandled;
5941 }
5942 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005943 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
5944 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005945 if (maxH > 0) {
5946 pinScrollBy(maxH, 0, true, 0);
5947 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005948 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
5949 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005950 if (maxH < 0) {
5951 pinScrollBy(maxH, 0, true, 0);
5952 }
5953 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005954 if (mLastCursorBounds.isEmpty()) return keyHandled;
5955 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
5956 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005957 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005958 if (DebugFlags.WEB_VIEW) {
5959 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
5960 + contentCursorRingBounds);
5961 }
5962 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005963 mUserScroll = true;
5964 return keyHandled;
5965 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005966
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005967 /**
5968 * Set the background color. It's white by default. Pass
5969 * zero to make the view transparent.
5970 * @param color the ARGB color described by Color.java
5971 */
5972 public void setBackgroundColor(int color) {
5973 mBackgroundColor = color;
5974 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
5975 }
5976
5977 public void debugDump() {
5978 nativeDebugDump();
5979 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
5980 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005981
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005982 /**
Mike Reedefe2c722009-10-14 09:42:02 -04005983 * Draw the HTML page into the specified canvas. This call ignores any
5984 * view-specific zoom, scroll offset, or other changes. It does not draw
5985 * any view-specific chrome, such as progress or URL bars.
5986 *
5987 * @hide only needs to be accessible to Browser and testing
5988 */
5989 public void drawPage(Canvas canvas) {
5990 mWebViewCore.drawContentPicture(canvas, 0, false, false);
5991 }
5992
5993 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005994 * Update our cache with updatedText.
5995 * @param updatedText The new text to put in our cache.
5996 */
5997 /* package */ void updateCachedTextfield(String updatedText) {
5998 // Also place our generation number so that when we look at the cache
5999 // we recognize that it is up to date.
6000 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
6001 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006002
Cary Clark1cb97ee2009-12-11 12:10:36 -05006003 private native int nativeCacheHitFramePointer();
6004 private native Rect nativeCacheHitNodeBounds();
6005 private native int nativeCacheHitNodePointer();
Leon Scroggins4890feb2009-07-02 10:37:10 -04006006 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006007 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04006008 private native int nativeCursorFramePointer();
6009 private native Rect nativeCursorNodeBounds();
6010 /* package */ native int nativeCursorNodePointer();
6011 /* package */ native boolean nativeCursorMatchesFocus();
6012 private native boolean nativeCursorIntersects(Rect visibleRect);
6013 private native boolean nativeCursorIsAnchor();
Leon Scroggins1d96ca02009-10-23 11:49:03 -04006014 private native boolean nativeCursorIsReadOnly();
Cary Clarkd6982c92009-05-29 11:02:22 -04006015 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04006016 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04006017 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006018 /**
6019 * Returns true if the native cursor node says it wants to handle key events
6020 * (ala plugins). This can only be called if mNativeClass is non-zero!
6021 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04006022 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006023 private native void nativeDebugDump();
6024 private native void nativeDestroy();
Cary Clarkd6982c92009-05-29 11:02:22 -04006025 private native void nativeDrawCursorRing(Canvas content);
6026 private native void nativeDrawMatches(Canvas canvas);
Cary Clark09e383c2009-10-26 16:43:58 -04006027 private native void nativeDrawSelectionPointer(Canvas content,
6028 float scale, int x, int y, boolean extendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006029 private native void nativeDrawSelectionRegion(Canvas content);
Cary Clarkd6982c92009-05-29 11:02:22 -04006030 private native void nativeDumpDisplayTree(String urlOrNull);
6031 private native int nativeFindAll(String findLower, String findUpper);
6032 private native void nativeFindNext(boolean forward);
Cary Clark3e88ddc2009-10-08 14:59:46 -04006033 private native int nativeFocusCandidateFramePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006034 private native boolean nativeFocusCandidateIsPassword();
Cary Clark3e88ddc2009-10-08 14:59:46 -04006035 private native boolean nativeFocusCandidateIsPlugin();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006036 private native boolean nativeFocusCandidateIsRtlText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006037 private native boolean nativeFocusCandidateIsTextInput();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05006038 /* package */ native int nativeFocusCandidateMaxLength();
Leon Scroggins0ca70882009-06-26 17:45:29 -04006039 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006040 private native Rect nativeFocusCandidateNodeBounds();
6041 /* package */ native int nativeFocusCandidatePointer();
6042 private native String nativeFocusCandidateText();
6043 private native int nativeFocusCandidateTextSize();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05006044 /**
6045 * Returns an integer corresponding to WebView.cpp::type.
6046 * See WebTextView.setType()
6047 */
6048 private native int nativeFocusCandidateType();
Derek Sollenberger718d69f2009-10-19 15:56:43 -04006049 private native boolean nativeFocusIsPlugin();
Leon Scroggins4890feb2009-07-02 10:37:10 -04006050 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04006051 private native Rect nativeGetCursorRingBounds();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006052 private native Region nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04006053 private native boolean nativeHasCursorNode();
6054 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04006055 private native void nativeHideCursor();
Cary Clarkd6982c92009-05-29 11:02:22 -04006056 private native String nativeImageURI(int x, int y);
6057 private native void nativeInstrumentReport();
Leon Scroggins01058282009-07-30 16:33:56 -04006058 /* package */ native void nativeMoveCursorToNextTextInput();
Cary Clarkd6982c92009-05-29 11:02:22 -04006059 // return true if the page has been scrolled
6060 private native boolean nativeMotionUp(int x, int y, int slop);
6061 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006062 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04006063 boolean noScroll);
6064 private native int nativeMoveGeneration();
6065 private native void nativeMoveSelection(int x, int y,
6066 boolean extendSelection);
Cary Clark1cb97ee2009-12-11 12:10:36 -05006067 private native boolean nativePointInNavCache(int x, int y, int slop);
Cary Clarkd6982c92009-05-29 11:02:22 -04006068 // Like many other of our native methods, you must make sure that
6069 // mNativeClass is not null before calling this method.
6070 private native void nativeRecordButtons(boolean focused,
6071 boolean pressed, boolean invalidate);
6072 private native void nativeSelectBestAt(Rect rect);
Cary Clark32847a92009-11-17 16:04:18 -05006073 private native void nativeSetFindIsUp();
Cary Clarkd6982c92009-05-29 11:02:22 -04006074 private native void nativeSetFollowedLink(boolean followed);
6075 private native void nativeSetHeightCanMeasure(boolean measure);
Leon Scroggins01058282009-07-30 16:33:56 -04006076 // Returns a value corresponding to CachedFrame::ImeAction
6077 /* package */ native int nativeTextFieldAction();
Cary Clarkd6982c92009-05-29 11:02:22 -04006078 private native int nativeTextGeneration();
6079 // Never call this version except by updateCachedTextfield(String) -
6080 // we always want to pass in our generation number.
6081 private native void nativeUpdateCachedTextfield(String updatedText,
6082 int generation);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006083 // return NO_LEFTEDGE means failure.
6084 private static final int NO_LEFTEDGE = -1;
Cary Clark77d98f42009-07-31 09:40:38 -04006085 private native int nativeGetBlockLeftEdge(int x, int y, float scale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006086}