blob: e23109fc9d6be888d4916e84ec02636525193fc4 [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;
Grace Kloba3a0def22010-01-23 21:11:54 -080024import android.content.pm.PackageManager;
Leon Scroggins3667ce42009-05-13 15:58:03 -040025import android.database.DataSetObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.graphics.Bitmap;
27import android.graphics.Canvas;
28import android.graphics.Color;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.graphics.Picture;
30import android.graphics.Point;
31import android.graphics.Rect;
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;
Grace Kloba3a0def22010-01-23 21:11:54 -080052import android.view.ScaleGestureDetector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.view.SoundEffectConstants;
54import android.view.VelocityTracker;
55import android.view.View;
56import android.view.ViewConfiguration;
57import android.view.ViewGroup;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080058import android.view.ViewTreeObserver;
The Android Open Source Project10592532009-03-18 17:39:46 -070059import android.view.animation.AlphaAnimation;
Derek Sollenberger7cabb032010-01-21 10:37:38 -050060import android.view.inputmethod.EditorInfo;
61import android.view.inputmethod.InputConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.view.inputmethod.InputMethodManager;
Leon Scrogginsd3465f62009-06-02 10:57:54 -040063import android.webkit.WebTextView.AutoCompleteAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.webkit.WebViewCore.EventHub;
65import android.widget.AbsoluteLayout;
Leon Scroggins3667ce42009-05-13 15:58:03 -040066import android.widget.Adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067import android.widget.AdapterView;
68import android.widget.ArrayAdapter;
Leon Scrogginsa8da1732009-10-19 19:04:30 -040069import android.widget.CheckedTextView;
The Android Open Source Project10592532009-03-18 17:39:46 -070070import android.widget.FrameLayout;
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040071import android.widget.LinearLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073import android.widget.Scroller;
74import android.widget.Toast;
75import android.widget.ZoomButtonsController;
The Android Open Source Project10592532009-03-18 17:39:46 -070076import android.widget.ZoomControls;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077import android.widget.AdapterView.OnItemClickListener;
78
79import java.io.File;
80import java.io.FileInputStream;
81import java.io.FileNotFoundException;
82import java.io.FileOutputStream;
83import java.io.IOException;
84import java.net.URLDecoder;
85import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086import java.util.List;
Andrei Popescuf5dba882010-01-12 22:42:41 +000087import java.util.HashMap;
Andrei Popescu4950b2b2009-09-03 13:56:07 +010088import java.util.Map;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040090import junit.framework.Assert;
91
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092/**
Cary Clarkd6982c92009-05-29 11:02:22 -040093 * <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 -080094 * can roll your own web browser or simply display some online content within your Activity.
95 * It uses the WebKit rendering engine to display
96 * web pages and includes methods to navigate forward and backward
97 * through a history, zoom in and out, perform text searches and more.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -070098 * <p>To enable the built-in zoom, set
99 * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
100 * (introduced in API version 3).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 * <p>Note that, in order for your Activity to access the Internet and load web pages
Cary Clarkd6982c92009-05-29 11:02:22 -0400102 * in a WebView, you must add the <var>INTERNET</var> permissions to your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800103 * Android Manifest file:</p>
104 * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100105 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 * <p>This must be a child of the <code>&lt;manifest></code> element.</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100107 *
108 * <h3>Basic usage</h3>
109 *
110 * <p>By default, a WebView provides no browser-like widgets, does not
111 * enable JavaScript and errors will be ignored. If your goal is only
112 * to display some HTML as a part of your UI, this is probably fine;
113 * the user won't need to interact with the web page beyond reading
114 * it, and the web page won't need to interact with the user. If you
115 * actually want a fully blown web browser, then you probably want to
116 * invoke the Browser application with your URL rather than show it
117 * with a WebView. See {@link android.content.Intent} for more information.</p>
118 *
119 * <pre class="prettyprint">
120 * WebView webview = new WebView(this);
121 * setContentView(webview);
122 *
123 * // Simplest usage: note that an exception will NOT be thrown
124 * // if there is an error loading this page (see below).
125 * webview.loadUrl("http://slashdot.org/");
126 *
127 * // Of course you can also load from any string:
128 * String summary = "&lt;html>&lt;body>You scored &lt;b>192</b> points.&lt;/body>&lt;/html>";
129 * webview.loadData(summary, "text/html", "utf-8");
130 * // ... although note that there are restrictions on what this HTML can do.
131 * // See the JavaDocs for loadData and loadDataWithBaseUrl for more info.
132 * </pre>
133 *
134 * <p>A WebView has several customization points where you can add your
135 * own behavior. These are:</p>
136 *
137 * <ul>
138 * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
139 * This class is called when something that might impact a
140 * browser UI happens, for instance, progress updates and
141 * JavaScript alerts are sent here.
142 * </li>
143 * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
144 * It will be called when things happen that impact the
145 * rendering of the content, eg, errors or form submissions. You
146 * can also intercept URL loading here.</li>
147 * <li>Via the {@link android.webkit.WebSettings} class, which contains
148 * miscellaneous configuration. </li>
149 * <li>With the {@link android.webkit.WebView#addJavascriptInterface} method.
150 * This lets you bind Java objects into the WebView so they can be
151 * controlled from the web pages JavaScript.</li>
152 * </ul>
153 *
154 * <p>Here's a more complicated example, showing error handling,
155 * settings, and progress notification:</p>
156 *
157 * <pre class="prettyprint">
158 * // Let's display the progress in the activity title bar, like the
159 * // browser app does.
160 * getWindow().requestFeature(Window.FEATURE_PROGRESS);
161 *
162 * webview.getSettings().setJavaScriptEnabled(true);
163 *
164 * final Activity activity = this;
165 * webview.setWebChromeClient(new WebChromeClient() {
166 * public void onProgressChanged(WebView view, int progress) {
167 * // Activities and WebViews measure progress with different scales.
168 * // The progress meter will automatically disappear when we reach 100%
169 * activity.setProgress(progress * 1000);
170 * }
171 * });
172 * webview.setWebViewClient(new WebViewClient() {
173 * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
174 * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
175 * }
176 * });
177 *
178 * webview.loadUrl("http://slashdot.org/");
179 * </pre>
180 *
181 * <h3>Cookie and window management</h3>
182 *
183 * <p>For obvious security reasons, your application has its own
184 * cache, cookie store etc - it does not share the Browser
185 * applications data. Cookies are managed on a separate thread, so
186 * operations like index building don't block the UI
187 * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
188 * if you want to use cookies in your application.
189 * </p>
190 *
191 * <p>By default, requests by the HTML to open new windows are
192 * ignored. This is true whether they be opened by JavaScript or by
193 * the target attribute on a link. You can customize your
194 * WebChromeClient to provide your own behaviour for opening multiple windows,
195 * and render them in whatever manner you want.</p>
196 *
197 * <p>Standard behavior for an Activity is to be destroyed and
198 * recreated when the devices orientation is changed. This will cause
199 * the WebView to reload the current page. If you don't want that, you
200 * can set your Activity to handle the orientation and keyboardHidden
201 * changes, and then just leave the WebView alone. It'll automatically
202 * re-orient itself as appropriate.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800203 */
Cary Clarkd6982c92009-05-29 11:02:22 -0400204public class WebView extends AbsoluteLayout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800205 implements ViewTreeObserver.OnGlobalFocusChangeListener,
206 ViewGroup.OnHierarchyChangeListener {
207
Mike Reedfdc54242010-01-08 10:29:51 -0500208 // enable debug output for drag trackers
Mike Reedb2924732010-01-08 17:34:58 -0500209 private static final boolean DEBUG_DRAG_TRACKER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
211 // the screen all-the-time. Good for profiling our drawing code
212 static private final boolean AUTO_REDRAW_HACK = false;
213 // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
214 private boolean mAutoRedraw;
Nicolas Roard38863332010-01-04 19:30:55 +0000215 private int mRootLayer; // C++ pointer to the root layer
216 private boolean mLayersHaveAnimations;
217 private EvaluateLayersAnimations mEvaluateThread;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 static final String LOGTAG = "webview";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800220
Leon Scroggins213a31c2009-05-21 16:49:32 -0700221 private static class ExtendedZoomControls extends FrameLayout {
The Android Open Source Project10592532009-03-18 17:39:46 -0700222 public ExtendedZoomControls(Context context, AttributeSet attrs) {
223 super(context, attrs);
224 LayoutInflater inflater = (LayoutInflater)
225 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
226 inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400227 mPlusMinusZoomControls = (ZoomControls) findViewById(
228 com.android.internal.R.id.zoomControls);
Grace Kloba04b28682009-09-14 14:38:37 -0700229 findViewById(com.android.internal.R.id.zoomMagnify).setVisibility(
230 View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700231 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400232
The Android Open Source Project10592532009-03-18 17:39:46 -0700233 public void show(boolean showZoom, boolean canZoomOut) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400234 mPlusMinusZoomControls.setVisibility(
235 showZoom ? View.VISIBLE : View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700236 fade(View.VISIBLE, 0.0f, 1.0f);
237 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400238
The Android Open Source Project10592532009-03-18 17:39:46 -0700239 public void hide() {
240 fade(View.GONE, 1.0f, 0.0f);
241 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400242
The Android Open Source Project10592532009-03-18 17:39:46 -0700243 private void fade(int visibility, float startAlpha, float endAlpha) {
244 AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
245 anim.setDuration(500);
246 startAnimation(anim);
247 setVisibility(visibility);
248 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400249
The Android Open Source Project10592532009-03-18 17:39:46 -0700250 public boolean hasFocus() {
Grace Kloba04b28682009-09-14 14:38:37 -0700251 return mPlusMinusZoomControls.hasFocus();
The Android Open Source Project10592532009-03-18 17:39:46 -0700252 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400253
The Android Open Source Project10592532009-03-18 17:39:46 -0700254 public void setOnZoomInClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400255 mPlusMinusZoomControls.setOnZoomInClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700256 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400257
The Android Open Source Project10592532009-03-18 17:39:46 -0700258 public void setOnZoomOutClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400259 mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700260 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400261
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400262 ZoomControls mPlusMinusZoomControls;
The Android Open Source Project10592532009-03-18 17:39:46 -0700263 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 /**
266 * Transportation object for returning WebView across thread boundaries.
267 */
268 public class WebViewTransport {
269 private WebView mWebview;
270
271 /**
272 * Set the WebView to the transportation object.
273 * @param webview The WebView to transport.
274 */
275 public synchronized void setWebView(WebView webview) {
276 mWebview = webview;
277 }
278
279 /**
280 * Return the WebView object.
281 * @return WebView The transported WebView object.
282 */
283 public synchronized WebView getWebView() {
284 return mWebview;
285 }
286 }
287
288 // A final CallbackProxy shared by WebViewCore and BrowserFrame.
289 private final CallbackProxy mCallbackProxy;
290
291 private final WebViewDatabase mDatabase;
292
293 // SSL certificate for the main top-level page (if secure)
294 private SslCertificate mCertificate;
295
296 // Native WebView pointer that is 0 until the native object has been
297 // created.
298 private int mNativeClass;
299 // This would be final but it needs to be set to null when the WebView is
300 // destroyed.
301 private WebViewCore mWebViewCore;
302 // Handler for dispatching UI messages.
303 /* package */ final Handler mPrivateHandler = new PrivateHandler();
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400304 private WebTextView mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305 // Used to ignore changes to webkit text that arrives to the UI side after
306 // more key events.
307 private int mTextGeneration;
308
Patrick Scott0a5ce012009-07-02 08:56:10 -0400309 // Used by WebViewCore to create child views.
310 /* package */ final ViewManager mViewManager;
311
Grace Kloba11438c32009-12-16 11:39:12 -0800312 // Used to display in full screen mode
313 PluginFullScreenHolder mFullScreenHolder;
314
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 /**
316 * Position of the last touch event.
317 */
318 private float mLastTouchX;
319 private float mLastTouchY;
320
321 /**
322 * Time of the last touch event.
323 */
324 private long mLastTouchTime;
325
326 /**
327 * Time of the last time sending touch event to WebViewCore
328 */
329 private long mLastSentTouchTime;
330
331 /**
332 * The minimum elapsed time before sending another ACTION_MOVE event to
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700333 * WebViewCore. This really should be tuned for each type of the devices.
334 * For example in Google Map api test case, it takes Dream device at least
335 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
336 * triggering the layout and drawing the picture. While the same process
337 * takes 60+ms on the current high speed device. If we make
338 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
339 * to WebViewCore queue and the real layout and draw events will be pushed
340 * to further, which slows down the refresh rate. Choose 50 to favor the
341 * current high speed devices. For Dream like devices, 100 is a better
342 * choice. Maybe make this in the buildspec later.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800343 */
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700344 private static final int TOUCH_SENT_INTERVAL = 50;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345
346 /**
347 * Helper class to get velocity for fling
348 */
349 VelocityTracker mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700350 private int mMaximumFling;
Cary Clark278ce052009-08-31 16:08:42 -0400351 private float mLastVelocity;
352 private float mLastVelX;
353 private float mLastVelY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 /**
356 * Touch mode
357 */
358 private int mTouchMode = TOUCH_DONE_MODE;
359 private static final int TOUCH_INIT_MODE = 1;
360 private static final int TOUCH_DRAG_START_MODE = 2;
361 private static final int TOUCH_DRAG_MODE = 3;
362 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
363 private static final int TOUCH_SHORTPRESS_MODE = 5;
Grace Kloba04b28682009-09-14 14:38:37 -0700364 private static final int TOUCH_DOUBLE_TAP_MODE = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 private static final int TOUCH_DONE_MODE = 7;
366 private static final int TOUCH_SELECT_MODE = 8;
Grace Kloba3a0def22010-01-23 21:11:54 -0800367 private static final int TOUCH_PINCH_DRAG = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368
369 // Whether to forward the touch events to WebCore
370 private boolean mForwardTouchEvents = false;
371
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 // Whether to prevent drag during touch. The initial value depends on
373 // mForwardTouchEvents. If WebCore wants touch events, we assume it will
374 // take control of touch events unless it says no for touch down event.
Grace Klobaf58af622009-09-24 17:41:23 -0700375 private static final int PREVENT_DRAG_NO = 0;
376 private static final int PREVENT_DRAG_MAYBE_YES = 1;
377 private static final int PREVENT_DRAG_YES = 2;
378 private int mPreventDrag = PREVENT_DRAG_NO;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800379
Grace Kloba5f68d6f2009-12-08 18:42:54 -0800380 // by default mPreventLongPress is false. If it is true, long press event
381 // will be handled by WebKit instead of UI.
382 private boolean mPreventLongPress = false;
383 // by default mPreventDoubleTap is false. If it is true, double tap event
384 // will be handled by WebKit instead of UI.
385 private boolean mPreventDoubleTap = false;
386
387 // this needs to be in sync with the logic in WebKit's
388 // EventHandler::handleTouchEvent()
389 private static final int TOUCH_PREVENT_DRAG = 0x1;
390 private static final int TOUCH_PREVENT_LONGPRESS = 0x2;
391 private static final int TOUCH_PREVENT_DOUBLETAP = 0x4;
392
Leon Scroggins72543e12009-07-23 15:29:45 -0400393 // To keep track of whether the current drag was initiated by a WebTextView,
394 // so that we know not to hide the cursor
395 boolean mDragFromTextInput;
396
Cary Clarkd6982c92009-05-29 11:02:22 -0400397 // Whether or not to draw the cursor ring.
398 private boolean mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399
Mike Reedd205d5b2009-05-27 11:02:29 -0400400 // true if onPause has been called (and not onResume)
401 private boolean mIsPaused;
402
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 /**
404 * Customizable constant
405 */
406 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
407 private int mTouchSlopSquare;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700408 // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
409 private int mDoubleTapSlopSquare;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700410 // pre-computed density adjusted navigation slop
411 private int mNavSlop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 // This should be ViewConfiguration.getTapTimeout()
413 // But system time out is 100ms, which is too short for the browser.
414 // In the browser, if it switches out of tap too soon, jump tap won't work.
415 private static final int TAP_TIMEOUT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 // This should be ViewConfiguration.getLongPressTimeout()
417 // But system time out is 500ms, which is too short for the browser.
418 // With a short timeout, it's difficult to treat trigger a short press.
419 private static final int LONG_PRESS_TIMEOUT = 1000;
420 // needed to avoid flinging after a pause of no movement
421 private static final int MIN_FLING_TIME = 250;
Cary Clark25415e22009-10-12 13:41:28 -0400422 // draw unfiltered after drag is held without movement
423 private static final int MOTIONLESS_TIME = 100;
The Android Open Source Project10592532009-03-18 17:39:46 -0700424 // The time that the Zoom Controls are visible before fading away
Cary Clarkd6982c92009-05-29 11:02:22 -0400425 private static final long ZOOM_CONTROLS_TIMEOUT =
The Android Open Source Project10592532009-03-18 17:39:46 -0700426 ViewConfiguration.getZoomControlsTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 // The amount of content to overlap between two screens when going through
428 // pages with the space bar, in pixels.
429 private static final int PAGE_SCROLL_OVERLAP = 24;
430
431 /**
432 * These prevent calling requestLayout if either dimension is fixed. This
433 * depends on the layout parameters and the measure specs.
434 */
435 boolean mWidthCanMeasure;
436 boolean mHeightCanMeasure;
437
438 // Remember the last dimensions we sent to the native side so we can avoid
439 // sending the same dimensions more than once.
440 int mLastWidthSent;
441 int mLastHeightSent;
442
443 private int mContentWidth; // cache of value from WebViewCore
444 private int mContentHeight; // cache of value from WebViewCore
445
Cary Clarkd6982c92009-05-29 11:02:22 -0400446 // Need to have the separate control for horizontal and vertical scrollbar
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 // style than the View's single scrollbar style
448 private boolean mOverlayHorizontalScrollbar = true;
449 private boolean mOverlayVerticalScrollbar = false;
450
451 // our standard speed. this way small distances will be traversed in less
452 // time than large distances, but we cap the duration, so that very large
453 // distances won't take too long to get there.
454 private static final int STD_SPEED = 480; // pixels per second
455 // time for the longest scroll animation
456 private static final int MAX_DURATION = 750; // milliseconds
Leon Scroggins03c87bf2009-09-18 15:05:59 -0400457 private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 private Scroller mScroller;
459
460 private boolean mWrapContent;
Cary Clark25415e22009-10-12 13:41:28 -0400461 private static final int MOTIONLESS_FALSE = 0;
462 private static final int MOTIONLESS_PENDING = 1;
463 private static final int MOTIONLESS_TRUE = 2;
464 private int mHeldMotionless;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465
Grace Kloba3a0def22010-01-23 21:11:54 -0800466 // whether support multi-touch
467 private static boolean mSupportMultiTouch;
468 // use the framework's ScaleGestureDetector to handle multi-touch
469 private ScaleGestureDetector mScaleDetector;
470 // minimum scale change during multi-touch zoom
471 private static float PREVIEW_SCALE_INCREMENT = 0.01f;
472
473 // the anchor point in the document space where VIEW_SIZE_CHANGED should
474 // apply to
475 private int mAnchorX;
476 private int mAnchorY;
477
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478 /**
479 * Private message ids
480 */
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400481 private static final int REMEMBER_PASSWORD = 1;
482 private static final int NEVER_REMEMBER_PASSWORD = 2;
483 private static final int SWITCH_TO_SHORTPRESS = 3;
484 private static final int SWITCH_TO_LONGPRESS = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700485 private static final int RELEASE_SINGLE_TAP = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400486 private static final int REQUEST_FORM_DATA = 6;
Grace Kloba96949ef2010-01-25 09:53:01 -0800487 private static final int RESUME_WEBCORE_PRIORITY = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400488 private static final int DRAG_HELD_MOTIONLESS = 8;
489 private static final int AWAKEN_SCROLL_BARS = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490
491 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400492 static final int SCROLL_TO_MSG_ID = 10;
493 static final int SCROLL_BY_MSG_ID = 11;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800494 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400495 static final int SPAWN_SCROLL_TO_MSG_ID = 12;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800496 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400497 static final int SYNC_SCROLL_TO_MSG_ID = 13;
498 static final int NEW_PICTURE_MSG_ID = 14;
499 static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
500 static final int WEBCORE_INITIALIZED_MSG_ID = 16;
501 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
Grace Kloba769ed212010-01-27 10:52:47 -0800502 static final int UPDATE_ZOOM_RANGE = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400503 static final int MOVE_OUT_OF_PLUGIN = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400504 static final int CLEAR_TEXT_ENTRY = 20;
Leon Scroggins6679f2f2009-08-12 18:48:10 -0400505 static final int UPDATE_TEXT_SELECTION_MSG_ID = 21;
Grace Kloba3a0def22010-01-23 21:11:54 -0800506 static final int SHOW_RECT_MSG_ID = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400507 static final int LONG_PRESS_CENTER = 23;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400508 static final int PREVENT_TOUCH_ID = 24;
509 static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 // obj=Rect in doc coordinates
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400511 static final int INVAL_RECT_MSG_ID = 26;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400512 static final int REQUEST_KEYBOARD = 27;
Cary Clark1cb97ee2009-12-11 12:10:36 -0500513 static final int DO_MOTION_UP = 28;
Grace Kloba11438c32009-12-16 11:39:12 -0800514 static final int SHOW_FULLSCREEN = 29;
515 static final int HIDE_FULLSCREEN = 30;
Leon Scrogginse26efa32009-12-15 16:38:45 -0500516 static final int DOM_FOCUS_CHANGED = 31;
Nicolas Roard38863332010-01-04 19:30:55 +0000517 static final int IMMEDIATE_REPAINT_MSG_ID = 32;
518 static final int SET_ROOT_LAYER_MSG_ID = 33;
Leon Scroggins3a503392010-01-06 17:04:38 -0500519 static final int RETURN_LABEL = 34;
Jean-Baptiste Querud09f4aa2010-01-28 15:14:57 -0800520 static final int FIND_AGAIN = 35;
Cary Clarkd6982c92009-05-29 11:02:22 -0400521
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800522 static final String[] HandlerDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400523 "REMEMBER_PASSWORD", // = 1;
524 "NEVER_REMEMBER_PASSWORD", // = 2;
525 "SWITCH_TO_SHORTPRESS", // = 3;
526 "SWITCH_TO_LONGPRESS", // = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700527 "RELEASE_SINGLE_TAP", // = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400528 "REQUEST_FORM_DATA", // = 6;
Grace Kloba96949ef2010-01-25 09:53:01 -0800529 "RESUME_WEBCORE_PRIORITY", // = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400530 "DRAG_HELD_MOTIONLESS", // = 8;
531 "AWAKEN_SCROLL_BARS", // = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 "SCROLL_TO_MSG_ID", // = 10;
533 "SCROLL_BY_MSG_ID", // = 11;
534 "SPAWN_SCROLL_TO_MSG_ID", // = 12;
535 "SYNC_SCROLL_TO_MSG_ID", // = 13;
536 "NEW_PICTURE_MSG_ID", // = 14;
537 "UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
538 "WEBCORE_INITIALIZED_MSG_ID", // = 16;
539 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
Grace Kloba769ed212010-01-27 10:52:47 -0800540 "UPDATE_ZOOM_RANGE", // = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400541 "MOVE_OUT_OF_PLUGIN", // = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400542 "CLEAR_TEXT_ENTRY", // = 20;
Leon Scroggins6679f2f2009-08-12 18:48:10 -0400543 "UPDATE_TEXT_SELECTION_MSG_ID", // = 21;
Grace Kloba3a0def22010-01-23 21:11:54 -0800544 "SHOW_RECT_MSG_ID", // = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400545 "LONG_PRESS_CENTER", // = 23;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800546 "PREVENT_TOUCH_ID", // = 24;
547 "WEBCORE_NEED_TOUCH_EVENTS", // = 25;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400548 "INVAL_RECT_MSG_ID", // = 26;
Cary Clark1cb97ee2009-12-11 12:10:36 -0500549 "REQUEST_KEYBOARD", // = 27;
Grace Kloba11438c32009-12-16 11:39:12 -0800550 "DO_MOTION_UP", // = 28;
551 "SHOW_FULLSCREEN", // = 29;
Leon Scrogginse26efa32009-12-15 16:38:45 -0500552 "HIDE_FULLSCREEN", // = 30;
Nicolas Roard38863332010-01-04 19:30:55 +0000553 "DOM_FOCUS_CHANGED", // = 31;
554 "IMMEDIATE_REPAINT_MSG_ID", // = 32;
Leon Scroggins3a503392010-01-06 17:04:38 -0500555 "SET_ROOT_LAYER_MSG_ID", // = 33;
Jean-Baptiste Querud09f4aa2010-01-28 15:14:57 -0800556 "RETURN_LABEL", // = 34;
557 "FIND_AGAIN" // = 35;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558 };
559
Grace Klobaa4fa1072009-11-09 12:01:50 -0800560 // If the site doesn't use the viewport meta tag to specify the viewport,
561 // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
562 static final int DEFAULT_VIEWPORT_WIDTH = 800;
563
564 // normally we try to fit the content to the minimum preferred width
565 // calculated by the Webkit. To avoid the bad behavior when some site's
566 // minimum preferred width keeps growing when changing the viewport width or
567 // the minimum preferred width is huge, an upper limit is needed.
568 static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
569
Grace Kloba25737912009-06-19 12:42:47 -0700570 // default scale limit. Depending on the display density
571 private static float DEFAULT_MAX_ZOOM_SCALE;
572 private static float DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 // scale limit, which can be set through viewport meta tag in the web page
Grace Kloba25737912009-06-19 12:42:47 -0700574 private float mMaxZoomScale;
575 private float mMinZoomScale;
Grace Klobae397a882009-08-06 12:04:14 -0700576 private boolean mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800577
578 // initial scale in percent. 0 means using default.
Grace Kloba16efce72009-11-10 15:49:03 -0800579 private int mInitialScaleInPercent = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700581 // while in the zoom overview mode, the page's width is fully fit to the
582 // current window. The page is alive, in another words, you can click to
583 // follow the links. Double tap will toggle between zoom overview mode and
584 // the last zoom scale.
585 boolean mInZoomOverview = false;
Leon Scrogginsf58ffac2009-08-13 15:34:06 -0400586
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700587 // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
588 // engadget always have wider mContentWidth no matter what viewport size is.
Grace Klobaa4fa1072009-11-09 12:01:50 -0800589 int mZoomOverviewWidth = DEFAULT_VIEWPORT_WIDTH;
Grace Kloba3a0def22010-01-23 21:11:54 -0800590 float mTextWrapScale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700591
Grace Kloba25737912009-06-19 12:42:47 -0700592 // default scale. Depending on the display density.
593 static int DEFAULT_SCALE_PERCENT;
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700594 private float mDefaultScale;
Grace Kloba25737912009-06-19 12:42:47 -0700595
Grace Kloba3a0def22010-01-23 21:11:54 -0800596 // set to true temporarily during ScaleGesture triggered zoom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 private boolean mPreviewZoomOnly = false;
598
599 // computed scale and inverse, from mZoomWidth.
Grace Kloba25737912009-06-19 12:42:47 -0700600 private float mActualScale;
601 private float mInvActualScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 // if this is non-zero, it is used on drawing rather than mActualScale
603 private float mZoomScale;
604 private float mInvInitialZoomScale;
605 private float mInvFinalZoomScale;
Grace Kloba675c7d22009-07-23 09:21:21 -0700606 private int mInitialScrollX;
607 private int mInitialScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 private long mZoomStart;
609 private static final int ZOOM_ANIMATION_LENGTH = 500;
610
611 private boolean mUserScroll = false;
612
613 private int mSnapScrollMode = SNAP_NONE;
Cary Clarkac492e12009-10-14 14:53:37 -0400614 private static final int SNAP_NONE = 0;
615 private static final int SNAP_LOCK = 1; // not a separate state
616 private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
617 private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400619
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 // Used to match key downs and key ups
621 private boolean mGotKeyDown;
622
623 /* package */ static boolean mLogEvent = true;
624 private static final int EVENT_LOG_ZOOM_LEVEL_CHANGE = 70101;
625 private static final int EVENT_LOG_DOUBLE_TAP_DURATION = 70102;
626
627 // for event log
628 private long mLastTouchUpTime = 0;
629
630 /**
631 * URI scheme for telephone number
632 */
633 public static final String SCHEME_TEL = "tel:";
634 /**
635 * URI scheme for email address
636 */
637 public static final String SCHEME_MAILTO = "mailto:";
638 /**
639 * URI scheme for map address
640 */
641 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 private int mBackgroundColor = Color.WHITE;
644
645 // Used to notify listeners of a new picture.
646 private PictureListener mPictureListener;
647 /**
648 * Interface to listen for new pictures as they change.
649 */
650 public interface PictureListener {
651 /**
652 * Notify the listener that the picture has changed.
653 * @param view The WebView that owns the picture.
654 * @param picture The new picture.
655 */
656 public void onNewPicture(WebView view, Picture picture);
657 }
658
Leon Scroggins3246c222009-05-26 09:51:23 -0400659 // FIXME: Want to make this public, but need to change the API file.
660 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661 /**
662 * Default HitTestResult, where the target is unknown
663 */
664 public static final int UNKNOWN_TYPE = 0;
665 /**
666 * HitTestResult for hitting a HTML::a tag
667 */
668 public static final int ANCHOR_TYPE = 1;
669 /**
670 * HitTestResult for hitting a phone number
671 */
672 public static final int PHONE_TYPE = 2;
673 /**
674 * HitTestResult for hitting a map address
675 */
676 public static final int GEO_TYPE = 3;
677 /**
678 * HitTestResult for hitting an email address
679 */
680 public static final int EMAIL_TYPE = 4;
681 /**
682 * HitTestResult for hitting an HTML::img tag
683 */
684 public static final int IMAGE_TYPE = 5;
685 /**
686 * HitTestResult for hitting a HTML::a tag which contains HTML::img
687 */
688 public static final int IMAGE_ANCHOR_TYPE = 6;
689 /**
690 * HitTestResult for hitting a HTML::a tag with src=http
691 */
692 public static final int SRC_ANCHOR_TYPE = 7;
693 /**
694 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
695 */
696 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
697 /**
698 * HitTestResult for hitting an edit text area
699 */
700 public static final int EDIT_TEXT_TYPE = 9;
701
702 private int mType;
703 private String mExtra;
704
705 HitTestResult() {
706 mType = UNKNOWN_TYPE;
707 }
708
709 private void setType(int type) {
710 mType = type;
711 }
712
713 private void setExtra(String extra) {
714 mExtra = extra;
715 }
716
717 public int getType() {
718 return mType;
719 }
720
721 public String getExtra() {
722 return mExtra;
723 }
724 }
725
The Android Open Source Project10592532009-03-18 17:39:46 -0700726 // The View containing the zoom controls
727 private ExtendedZoomControls mZoomControls;
728 private Runnable mZoomControlRunnable;
729
Cary Clarkd6982c92009-05-29 11:02:22 -0400730 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700732 // These keep track of the center point of the zoom. They are used to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 // determine the point around which we should zoom.
734 private float mZoomCenterX;
735 private float mZoomCenterY;
736
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700737 private ZoomButtonsController.OnZoomListener mZoomListener =
738 new ZoomButtonsController.OnZoomListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 public void onVisibilityChanged(boolean visible) {
741 if (visible) {
742 switchOutDrawHistory();
Mike Reed8b302092009-11-12 12:50:20 -0500743 // Bring back the hidden zoom controls.
744 mZoomButtonsController.getZoomControls().setVisibility(
745 View.VISIBLE);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700746 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800747 }
748 }
749
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700750 public void onZoom(boolean zoomIn) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751 if (zoomIn) {
752 zoomIn();
753 } else {
754 zoomOut();
755 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400756
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700757 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 };
Cary Clarkd6982c92009-05-29 11:02:22 -0400760
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 /**
762 * Construct a new WebView with a Context object.
763 * @param context A Context object used to access application assets.
764 */
765 public WebView(Context context) {
766 this(context, null);
767 }
768
769 /**
770 * Construct a new WebView with layout parameters.
771 * @param context A Context object used to access application assets.
772 * @param attrs An AttributeSet passed to our parent.
773 */
774 public WebView(Context context, AttributeSet attrs) {
775 this(context, attrs, com.android.internal.R.attr.webViewStyle);
776 }
777
778 /**
779 * Construct a new WebView with layout parameters and a default style.
780 * @param context A Context object used to access application assets.
781 * @param attrs An AttributeSet passed to our parent.
782 * @param defStyle The default style resource ID.
783 */
784 public WebView(Context context, AttributeSet attrs, int defStyle) {
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100785 this(context, attrs, defStyle, null);
786 }
787
788 /**
789 * Construct a new WebView with layout parameters, a default style and a set
790 * of custom Javscript interfaces to be added to the WebView at initialization
Romain Guy01d0fbf2009-12-01 14:52:19 -0800791 * time. This guarantees that these interfaces will be available when the JS
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100792 * context is initialized.
793 * @param context A Context object used to access application assets.
794 * @param attrs An AttributeSet passed to our parent.
795 * @param defStyle The default style resource ID.
796 * @param javascriptInterfaces is a Map of intareface names, as keys, and
797 * object implementing those interfaces, as values.
798 * @hide pending API council approval.
799 */
800 protected WebView(Context context, AttributeSet attrs, int defStyle,
801 Map<String, Object> javascriptInterfaces) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 super(context, attrs, defStyle);
803 init();
804
805 mCallbackProxy = new CallbackProxy(context, this);
Grace Kloba9a67c822009-12-20 11:33:58 -0800806 mViewManager = new ViewManager(this);
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100807 mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808 mDatabase = WebViewDatabase.getInstance(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 mScroller = new Scroller(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700810
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700811 mZoomButtonsController = new ZoomButtonsController(this);
812 mZoomButtonsController.setOnZoomListener(mZoomListener);
Leon Scrogginsaa3f96a2009-06-11 14:46:35 -0400813 // ZoomButtonsController positions the buttons at the bottom, but in
814 // the middle. Change their layout parameters so they appear on the
815 // right.
816 View controls = mZoomButtonsController.getZoomControls();
817 ViewGroup.LayoutParams params = controls.getLayoutParams();
818 if (params instanceof FrameLayout.LayoutParams) {
819 FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
820 params;
821 frameParams.gravity = Gravity.RIGHT;
822 }
Grace Kloba3a0def22010-01-23 21:11:54 -0800823 updateMultiTouchSupport(context);
824 }
825
826 void updateMultiTouchSupport(Context context) {
827 WebSettings settings = getSettings();
828 mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
829 PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
830 && settings.supportZoom() && settings.getBuiltInZoomControls();
831 if (mSupportMultiTouch && (mScaleDetector == null)) {
832 mScaleDetector = new ScaleGestureDetector(context,
833 new ScaleDetectorListener());
834 } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
835 mScaleDetector = null;
836 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700837 }
838
839 private void updateZoomButtonsEnabled() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700840 boolean canZoomIn = mActualScale < mMaxZoomScale;
Grace Kloba455e3af2009-08-13 11:01:21 -0700841 boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview;
The Android Open Source Project10592532009-03-18 17:39:46 -0700842 if (!canZoomIn && !canZoomOut) {
843 // Hide the zoom in and out buttons, as well as the fit to page
844 // button, if the page cannot zoom
845 mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700846 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -0700847 // Set each one individually, as a page may be able to zoom in
848 // or out.
849 mZoomButtonsController.setZoomInEnabled(canZoomIn);
850 mZoomButtonsController.setZoomOutEnabled(canZoomOut);
The Android Open Source Project10592532009-03-18 17:39:46 -0700851 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 }
853
854 private void init() {
855 setWillNotDraw(false);
856 setFocusable(true);
857 setFocusableInTouchMode(true);
858 setClickable(true);
859 setLongClickable(true);
860
Romain Guy4296fc42009-07-06 11:48:52 -0700861 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700862 int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 mTouchSlopSquare = slop * slop;
864 mMinLockSnapReverseDistance = slop;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700865 slop = configuration.getScaledDoubleTapSlop();
866 mDoubleTapSlopSquare = slop * slop;
Grace Kloba25737912009-06-19 12:42:47 -0700867 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700868 // use one line height, 16 based on our current default font, for how
869 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -0700870 mNavSlop = (int) (16 * density);
871 // density adjusted scale factors
872 DEFAULT_SCALE_PERCENT = (int) (100 * density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700873 mDefaultScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700874 mActualScale = density;
875 mInvActualScale = 1 / density;
Grace Kloba3a0def22010-01-23 21:11:54 -0800876 mTextWrapScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700877 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
878 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
879 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
880 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
Romain Guy4296fc42009-07-06 11:48:52 -0700881 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 }
883
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700884 /* package */void updateDefaultZoomDensity(int zoomDensity) {
885 final float density = getContext().getResources().getDisplayMetrics().density
886 * 100 / zoomDensity;
887 if (Math.abs(density - mDefaultScale) > 0.01) {
888 float scaleFactor = density / mDefaultScale;
889 // adjust the limits
890 mNavSlop = (int) (16 * density);
891 DEFAULT_SCALE_PERCENT = (int) (100 * density);
892 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
893 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
894 mDefaultScale = density;
895 mMaxZoomScale *= scaleFactor;
896 mMinZoomScale *= scaleFactor;
Grace Kloba3a0def22010-01-23 21:11:54 -0800897 setNewZoomScale(mActualScale * scaleFactor, true, false);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700898 }
899 }
900
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 /* package */ boolean onSavePassword(String schemePlusHost, String username,
902 String password, final Message resumeMsg) {
903 boolean rVal = false;
904 if (resumeMsg == null) {
905 // null resumeMsg implies saving password silently
906 mDatabase.setUsernamePassword(schemePlusHost, username, password);
907 } else {
908 final Message remember = mPrivateHandler.obtainMessage(
909 REMEMBER_PASSWORD);
910 remember.getData().putString("host", schemePlusHost);
911 remember.getData().putString("username", username);
912 remember.getData().putString("password", password);
913 remember.obj = resumeMsg;
914
915 final Message neverRemember = mPrivateHandler.obtainMessage(
916 NEVER_REMEMBER_PASSWORD);
917 neverRemember.getData().putString("host", schemePlusHost);
918 neverRemember.getData().putString("username", username);
919 neverRemember.getData().putString("password", password);
920 neverRemember.obj = resumeMsg;
921
922 new AlertDialog.Builder(getContext())
923 .setTitle(com.android.internal.R.string.save_password_label)
924 .setMessage(com.android.internal.R.string.save_password_message)
925 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
926 new DialogInterface.OnClickListener() {
927 public void onClick(DialogInterface dialog, int which) {
928 resumeMsg.sendToTarget();
929 }
930 })
931 .setNeutralButton(com.android.internal.R.string.save_password_remember,
932 new DialogInterface.OnClickListener() {
933 public void onClick(DialogInterface dialog, int which) {
934 remember.sendToTarget();
935 }
936 })
937 .setNegativeButton(com.android.internal.R.string.save_password_never,
938 new DialogInterface.OnClickListener() {
939 public void onClick(DialogInterface dialog, int which) {
940 neverRemember.sendToTarget();
941 }
942 })
943 .setOnCancelListener(new OnCancelListener() {
944 public void onCancel(DialogInterface dialog) {
945 resumeMsg.sendToTarget();
946 }
947 }).show();
948 // Return true so that WebViewCore will pause while the dialog is
949 // up.
950 rVal = true;
951 }
952 return rVal;
953 }
954
955 @Override
956 public void setScrollBarStyle(int style) {
957 if (style == View.SCROLLBARS_INSIDE_INSET
958 || style == View.SCROLLBARS_OUTSIDE_INSET) {
959 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
960 } else {
961 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
962 }
963 super.setScrollBarStyle(style);
964 }
965
966 /**
967 * Specify whether the horizontal scrollbar has overlay style.
968 * @param overlay TRUE if horizontal scrollbar should have overlay style.
969 */
970 public void setHorizontalScrollbarOverlay(boolean overlay) {
971 mOverlayHorizontalScrollbar = overlay;
972 }
973
974 /**
975 * Specify whether the vertical scrollbar has overlay style.
976 * @param overlay TRUE if vertical scrollbar should have overlay style.
977 */
978 public void setVerticalScrollbarOverlay(boolean overlay) {
979 mOverlayVerticalScrollbar = overlay;
980 }
981
982 /**
983 * Return whether horizontal scrollbar has overlay style
984 * @return TRUE if horizontal scrollbar has overlay style.
985 */
986 public boolean overlayHorizontalScrollbar() {
987 return mOverlayHorizontalScrollbar;
988 }
989
990 /**
991 * Return whether vertical scrollbar has overlay style
992 * @return TRUE if vertical scrollbar has overlay style.
993 */
994 public boolean overlayVerticalScrollbar() {
995 return mOverlayVerticalScrollbar;
996 }
997
998 /*
999 * Return the width of the view where the content of WebView should render
1000 * to.
Grace Kloba6ed525e2009-09-17 15:31:12 -07001001 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 */
Grace Kloba6ed525e2009-09-17 15:31:12 -07001003 /* package */ int getViewWidth() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
1005 return getWidth();
1006 } else {
1007 return getWidth() - getVerticalScrollbarWidth();
1008 }
1009 }
1010
1011 /*
Mike Reede8853fc2009-09-04 14:01:48 -04001012 * returns the height of the titlebarview (if any). Does not care about
1013 * scrolling
1014 */
1015 private int getTitleHeight() {
1016 return mTitleBar != null ? mTitleBar.getHeight() : 0;
1017 }
1018
1019 /*
1020 * Return the amount of the titlebarview (if any) that is visible
1021 */
1022 private int getVisibleTitleHeight() {
1023 return Math.max(getTitleHeight() - mScrollY, 0);
1024 }
1025
1026 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027 * Return the height of the view where the content of WebView should render
Leon Scroggins0236e672009-09-02 21:12:08 -04001028 * to. Note that this excludes mTitleBar, if there is one.
Grace Kloba6ed525e2009-09-17 15:31:12 -07001029 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 */
Grace Kloba6ed525e2009-09-17 15:31:12 -07001031 /* package */ int getViewHeight() {
Grace Kloba8eff73f2009-09-24 09:34:32 -07001032 return getViewHeightWithTitle() - getVisibleTitleHeight();
1033 }
1034
1035 private int getViewHeightWithTitle() {
Leon Scroggins0236e672009-09-02 21:12:08 -04001036 int height = getHeight();
Mike Reede8853fc2009-09-04 14:01:48 -04001037 if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001038 height -= getHorizontalScrollbarHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 }
Grace Kloba8eff73f2009-09-24 09:34:32 -07001040 return height;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 }
1042
1043 /**
1044 * @return The SSL certificate for the main top-level page or null if
1045 * there is no certificate (the site is not secure).
1046 */
1047 public SslCertificate getCertificate() {
1048 return mCertificate;
1049 }
1050
1051 /**
1052 * Sets the SSL certificate for the main top-level page.
1053 */
1054 public void setCertificate(SslCertificate certificate) {
1055 // here, the certificate can be null (if the site is not secure)
1056 mCertificate = certificate;
1057 }
1058
1059 //-------------------------------------------------------------------------
1060 // Methods called by activity
1061 //-------------------------------------------------------------------------
1062
1063 /**
1064 * Save the username and password for a particular host in the WebView's
1065 * internal database.
1066 * @param host The host that required the credentials.
1067 * @param username The username for the given host.
1068 * @param password The password for the given host.
1069 */
1070 public void savePassword(String host, String username, String password) {
1071 mDatabase.setUsernamePassword(host, username, password);
1072 }
1073
1074 /**
1075 * Set the HTTP authentication credentials for a given host and realm.
1076 *
1077 * @param host The host for the credentials.
1078 * @param realm The realm for the credentials.
1079 * @param username The username for the password. If it is null, it means
1080 * password can't be saved.
1081 * @param password The password
1082 */
1083 public void setHttpAuthUsernamePassword(String host, String realm,
1084 String username, String password) {
1085 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
1086 }
1087
1088 /**
1089 * Retrieve the HTTP authentication username and password for a given
1090 * host & realm pair
1091 *
1092 * @param host The host for which the credentials apply.
1093 * @param realm The realm for which the credentials apply.
1094 * @return String[] if found, String[0] is username, which can be null and
1095 * String[1] is password. Return null if it can't find anything.
1096 */
1097 public String[] getHttpAuthUsernamePassword(String host, String realm) {
1098 return mDatabase.getHttpAuthUsernamePassword(host, realm);
1099 }
1100
1101 /**
1102 * Destroy the internal state of the WebView. This method should be called
1103 * after the WebView has been removed from the view system. No other
1104 * methods may be called on a WebView after destroy.
1105 */
1106 public void destroy() {
1107 clearTextEntry();
1108 if (mWebViewCore != null) {
1109 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -04001110 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 mCallbackProxy.setWebViewClient(null);
1112 mCallbackProxy.setWebChromeClient(null);
1113 // Tell WebViewCore to destroy itself
1114 WebViewCore webViewCore = mWebViewCore;
1115 mWebViewCore = null; // prevent using partial webViewCore
1116 webViewCore.destroy();
1117 // Remove any pending messages that might not be serviced yet.
1118 mPrivateHandler.removeCallbacksAndMessages(null);
1119 mCallbackProxy.removeCallbacksAndMessages(null);
1120 // Wake up the WebCore thread just in case it is waiting for a
1121 // javascript dialog.
1122 synchronized (mCallbackProxy) {
1123 mCallbackProxy.notify();
1124 }
1125 }
1126 if (mNativeClass != 0) {
1127 nativeDestroy();
1128 mNativeClass = 0;
1129 }
1130 }
1131
1132 /**
1133 * Enables platform notifications of data state and proxy changes.
1134 */
1135 public static void enablePlatformNotifications() {
1136 Network.enablePlatformNotifications();
1137 }
1138
1139 /**
1140 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001141 * from the Activity's onPause() or onStop().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 */
1143 public static void disablePlatformNotifications() {
1144 Network.disablePlatformNotifications();
1145 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001146
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 /**
Feng Qianb3081372009-06-29 15:55:18 -07001148 * Sets JavaScript engine flags.
1149 *
1150 * @param flags JS engine flags in a String
1151 *
1152 * @hide pending API solidification
1153 */
1154 public void setJsFlags(String flags) {
1155 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1156 }
1157
1158 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159 * Inform WebView of the network state. This is used to set
1160 * the javascript property window.navigator.isOnline and
1161 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1162 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 */
1164 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001165 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1166 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 }
1168
1169 /**
Andrei Popescuf5dba882010-01-12 22:42:41 +00001170 * Inform WebView about the current network type.
1171 * {@hide}
1172 */
1173 public void setNetworkType(String type, String subtype) {
1174 Map<String, String> map = new HashMap<String, String>();
1175 map.put("type", type);
1176 map.put("subtype", subtype);
1177 mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
1178 }
1179 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001180 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001181 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1182 * method no longer stores the display data for this WebView. The previous
1183 * behavior could potentially leak files if {@link #restoreState} was never
1184 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1185 * and restoring the display data.
1186 * @param outState The Bundle to store the WebView state.
1187 * @return The same copy of the back/forward list used to save the state. If
1188 * saveState fails, the returned list will be null.
1189 * @see #savePicture
1190 * @see #restorePicture
1191 */
1192 public WebBackForwardList saveState(Bundle outState) {
1193 if (outState == null) {
1194 return null;
1195 }
1196 // We grab a copy of the back/forward list because a client of WebView
1197 // may have invalidated the history list by calling clearHistory.
1198 WebBackForwardList list = copyBackForwardList();
1199 final int currentIndex = list.getCurrentIndex();
1200 final int size = list.getSize();
1201 // We should fail saving the state if the list is empty or the index is
1202 // not in a valid range.
1203 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1204 return null;
1205 }
1206 outState.putInt("index", currentIndex);
1207 // FIXME: This should just be a byte[][] instead of ArrayList but
1208 // Parcel.java does not have the code to handle multi-dimensional
1209 // arrays.
1210 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1211 for (int i = 0; i < size; i++) {
1212 WebHistoryItem item = list.getItemAtIndex(i);
Cary Clark4fbf81b2009-09-29 16:07:56 -04001213 if (null == item) {
1214 // FIXME: this shouldn't happen
1215 // need to determine how item got set to null
1216 Log.w(LOGTAG, "saveState: Unexpected null history item.");
1217 return null;
1218 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 byte[] data = item.getFlattenedData();
1220 if (data == null) {
1221 // It would be very odd to not have any data for a given history
1222 // item. And we will fail to rebuild the history list without
1223 // flattened data.
1224 return null;
1225 }
1226 history.add(data);
1227 }
1228 outState.putSerializable("history", history);
1229 if (mCertificate != null) {
1230 outState.putBundle("certificate",
1231 SslCertificate.saveState(mCertificate));
1232 }
1233 return list;
1234 }
1235
1236 /**
1237 * Save the current display data to the Bundle given. Used in conjunction
1238 * with {@link #saveState}.
1239 * @param b A Bundle to store the display data.
1240 * @param dest The file to store the serialized picture data. Will be
1241 * overwritten with this WebView's picture data.
1242 * @return True if the picture was successfully saved.
1243 */
1244 public boolean savePicture(Bundle b, File dest) {
1245 if (dest == null || b == null) {
1246 return false;
1247 }
1248 final Picture p = capturePicture();
1249 try {
1250 final FileOutputStream out = new FileOutputStream(dest);
1251 p.writeToStream(out);
1252 out.close();
1253 } catch (FileNotFoundException e){
1254 e.printStackTrace();
1255 } catch (IOException e) {
1256 e.printStackTrace();
1257 } catch (RuntimeException e) {
1258 e.printStackTrace();
1259 }
1260 if (dest.length() > 0) {
1261 b.putInt("scrollX", mScrollX);
1262 b.putInt("scrollY", mScrollY);
1263 b.putFloat("scale", mActualScale);
Grace Kloba3a0def22010-01-23 21:11:54 -08001264 b.putFloat("textwrapScale", mTextWrapScale);
1265 b.putBoolean("overview", mInZoomOverview);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 return true;
1267 }
1268 return false;
1269 }
1270
1271 /**
1272 * Restore the display data that was save in {@link #savePicture}. Used in
1273 * conjunction with {@link #restoreState}.
1274 * @param b A Bundle containing the saved display data.
1275 * @param src The file where the picture data was stored.
1276 * @return True if the picture was successfully restored.
1277 */
1278 public boolean restorePicture(Bundle b, File src) {
1279 if (src == null || b == null) {
1280 return false;
1281 }
1282 if (src.exists()) {
1283 Picture p = null;
1284 try {
1285 final FileInputStream in = new FileInputStream(src);
1286 p = Picture.createFromStream(in);
1287 in.close();
1288 } catch (FileNotFoundException e){
1289 e.printStackTrace();
1290 } catch (RuntimeException e) {
1291 e.printStackTrace();
1292 } catch (IOException e) {
1293 e.printStackTrace();
1294 }
1295 if (p != null) {
1296 int sx = b.getInt("scrollX", 0);
1297 int sy = b.getInt("scrollY", 0);
1298 float scale = b.getFloat("scale", 1.0f);
1299 mDrawHistory = true;
1300 mHistoryPicture = p;
1301 mScrollX = sx;
1302 mScrollY = sy;
1303 mHistoryWidth = Math.round(p.getWidth() * scale);
1304 mHistoryHeight = Math.round(p.getHeight() * scale);
1305 // as getWidth() / getHeight() of the view are not
1306 // available yet, set up mActualScale, so that when
1307 // onSizeChanged() is called, the rest will be set
1308 // correctly
1309 mActualScale = scale;
Grace Kloba3a0def22010-01-23 21:11:54 -08001310 mTextWrapScale = b.getFloat("textwrapScale", scale);
1311 mInZoomOverview = b.getBoolean("overview");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001312 invalidate();
1313 return true;
1314 }
1315 }
1316 return false;
1317 }
1318
1319 /**
1320 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001321 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1322 * be called to restore the state of the WebView before using the object. If
1323 * it is called after the WebView has had a chance to build state (load
1324 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001325 * side-effects. Please note that this method no longer restores the
1326 * display data for this WebView. See {@link #savePicture} and {@link
1327 * #restorePicture} for saving and restoring the display data.
1328 * @param inState The incoming Bundle of state.
1329 * @return The restored back/forward list or null if restoreState failed.
1330 * @see #savePicture
1331 * @see #restorePicture
1332 */
1333 public WebBackForwardList restoreState(Bundle inState) {
1334 WebBackForwardList returnList = null;
1335 if (inState == null) {
1336 return returnList;
1337 }
1338 if (inState.containsKey("index") && inState.containsKey("history")) {
1339 mCertificate = SslCertificate.restoreState(
1340 inState.getBundle("certificate"));
1341
1342 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1343 final int index = inState.getInt("index");
1344 // We can't use a clone of the list because we need to modify the
1345 // shared copy, so synchronize instead to prevent concurrent
1346 // modifications.
1347 synchronized (list) {
1348 final List<byte[]> history =
1349 (List<byte[]>) inState.getSerializable("history");
1350 final int size = history.size();
1351 // Check the index bounds so we don't crash in native code while
1352 // restoring the history index.
1353 if (index < 0 || index >= size) {
1354 return null;
1355 }
1356 for (int i = 0; i < size; i++) {
1357 byte[] data = history.remove(0);
1358 if (data == null) {
1359 // If we somehow have null data, we cannot reconstruct
1360 // the item and thus our history list cannot be rebuilt.
1361 return null;
1362 }
1363 WebHistoryItem item = new WebHistoryItem(data);
1364 list.addHistoryItem(item);
1365 }
1366 // Grab the most recent copy to return to the caller.
1367 returnList = copyBackForwardList();
1368 // Update the copy to have the correct index.
1369 returnList.setCurrentIndex(index);
1370 }
1371 // Remove all pending messages because we are restoring previous
1372 // state.
1373 mWebViewCore.removeMessages();
1374 // Send a restore state message.
1375 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1376 }
1377 return returnList;
1378 }
1379
1380 /**
Grace Klobad0d9bc22010-01-26 18:08:28 -08001381 * Load the given url with the extra headers.
1382 * @param url The url of the resource to load.
1383 * @param extraHeaders The extra headers sent with this url. This should not
1384 * include the common headers like "user-agent". If it does, it
1385 * will be replaced by the intrinsic value of the WebView.
1386 */
1387 public void loadUrl(String url, Map<String, String> extraHeaders) {
1388 switchOutDrawHistory();
1389 WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
1390 arg.mUrl = url;
1391 arg.mExtraHeaders = extraHeaders;
1392 mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
1393 clearTextEntry();
1394 }
1395
1396 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001397 * Load the given url.
1398 * @param url The url of the resource to load.
1399 */
1400 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001401 if (url == null) {
1402 return;
1403 }
Grace Klobad0d9bc22010-01-26 18:08:28 -08001404 loadUrl(url, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001405 }
1406
1407 /**
Grace Kloba57534302009-05-22 18:55:02 -07001408 * Load the url with postData using "POST" method into the WebView. If url
1409 * is not a network url, it will be loaded with {link
1410 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001411 *
Grace Kloba57534302009-05-22 18:55:02 -07001412 * @param url The url of the resource to load.
1413 * @param postData The data will be passed to "POST" request.
Grace Kloba57534302009-05-22 18:55:02 -07001414 */
1415 public void postUrl(String url, byte[] postData) {
1416 if (URLUtil.isNetworkUrl(url)) {
1417 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001418 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1419 arg.mUrl = url;
1420 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001421 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
1422 clearTextEntry();
1423 } else {
1424 loadUrl(url);
1425 }
1426 }
1427
1428 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001429 * Load the given data into the WebView. This will load the data into
1430 * WebView using the data: scheme. Content loaded through this mechanism
1431 * does not have the ability to load content from the network.
1432 * @param data A String of data in the given encoding.
1433 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1434 * @param encoding The encoding of the data. i.e. utf-8, base64
1435 */
1436 public void loadData(String data, String mimeType, String encoding) {
1437 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1438 }
1439
1440 /**
1441 * Load the given data into the WebView, use the provided URL as the base
1442 * URL for the content. The base URL is the URL that represents the page
1443 * that is loaded through this interface. As such, it is used for the
1444 * history entry and to resolve any relative URLs. The failUrl is used if
1445 * browser fails to load the data provided. If it is empty or null, and the
1446 * load fails, then no history entry is created.
1447 * <p>
1448 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1449 * files through "file:///android_asset/" for the sub resources is more
1450 * restricted. If you provide null or empty string as baseUrl, you won't be
1451 * able to access asset files. If the baseUrl is anything other than
1452 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1453 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001454 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001455 * @param baseUrl Url to resolve relative paths with, if null defaults to
1456 * "about:blank"
1457 * @param data A String of data in the given encoding.
1458 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1459 * defaults to "text/html"
1460 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
1461 * @param failUrl URL to use if the content fails to load or null.
1462 */
1463 public void loadDataWithBaseURL(String baseUrl, String data,
1464 String mimeType, String encoding, String failUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001465
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001466 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1467 loadData(data, mimeType, encoding);
1468 return;
1469 }
1470 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001471 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1472 arg.mBaseUrl = baseUrl;
1473 arg.mData = data;
1474 arg.mMimeType = mimeType;
1475 arg.mEncoding = encoding;
1476 arg.mFailUrl = failUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001477 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
1478 clearTextEntry();
1479 }
1480
1481 /**
1482 * Stop the current load.
1483 */
1484 public void stopLoading() {
1485 // TODO: should we clear all the messages in the queue before sending
1486 // STOP_LOADING?
1487 switchOutDrawHistory();
1488 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1489 }
1490
1491 /**
1492 * Reload the current url.
1493 */
1494 public void reload() {
Leon Scroggins74077c82009-09-14 18:56:48 -04001495 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 switchOutDrawHistory();
1497 mWebViewCore.sendMessage(EventHub.RELOAD);
1498 }
1499
1500 /**
1501 * Return true if this WebView has a back history item.
1502 * @return True iff this WebView has a back history item.
1503 */
1504 public boolean canGoBack() {
1505 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1506 synchronized (l) {
1507 if (l.getClearPending()) {
1508 return false;
1509 } else {
1510 return l.getCurrentIndex() > 0;
1511 }
1512 }
1513 }
1514
1515 /**
1516 * Go back in the history of this WebView.
1517 */
1518 public void goBack() {
1519 goBackOrForward(-1);
1520 }
1521
1522 /**
1523 * Return true if this WebView has a forward history item.
1524 * @return True iff this Webview has a forward history item.
1525 */
1526 public boolean canGoForward() {
1527 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1528 synchronized (l) {
1529 if (l.getClearPending()) {
1530 return false;
1531 } else {
1532 return l.getCurrentIndex() < l.getSize() - 1;
1533 }
1534 }
1535 }
1536
1537 /**
1538 * Go forward in the history of this WebView.
1539 */
1540 public void goForward() {
1541 goBackOrForward(1);
1542 }
1543
1544 /**
1545 * Return true if the page can go back or forward the given
1546 * number of steps.
1547 * @param steps The negative or positive number of steps to move the
1548 * history.
1549 */
1550 public boolean canGoBackOrForward(int steps) {
1551 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1552 synchronized (l) {
1553 if (l.getClearPending()) {
1554 return false;
1555 } else {
1556 int newIndex = l.getCurrentIndex() + steps;
1557 return newIndex >= 0 && newIndex < l.getSize();
1558 }
1559 }
1560 }
1561
1562 /**
1563 * Go to the history item that is the number of steps away from
1564 * the current item. Steps is negative if backward and positive
1565 * if forward.
1566 * @param steps The number of steps to take back or forward in the back
1567 * forward list.
1568 */
1569 public void goBackOrForward(int steps) {
1570 goBackOrForward(steps, false);
1571 }
1572
1573 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
1574 // every time we go back or forward, we want to reset the
1575 // WebView certificate:
1576 // if the new site is secure, we will reload it and get a
1577 // new certificate set;
1578 // if the new site is not secure, the certificate must be
1579 // null, and that will be the case
1580 mCertificate = null;
1581 if (steps != 0) {
1582 clearTextEntry();
1583 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
1584 ignoreSnapshot ? 1 : 0);
1585 }
1586 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001587
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001588 private boolean extendScroll(int y) {
1589 int finalY = mScroller.getFinalY();
1590 int newY = pinLocY(finalY + y);
1591 if (newY == finalY) return false;
1592 mScroller.setFinalY(newY);
1593 mScroller.extendDuration(computeDuration(0, y));
1594 return true;
1595 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001596
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597 /**
1598 * Scroll the contents of the view up by half the view size
1599 * @param top true to jump to the top of the page
1600 * @return true if the page was scrolled
1601 */
1602 public boolean pageUp(boolean top) {
1603 if (mNativeClass == 0) {
1604 return false;
1605 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001606 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001607 if (top) {
1608 // go to the top of the document
1609 return pinScrollTo(mScrollX, 0, true, 0);
1610 }
1611 // Page up
1612 int h = getHeight();
1613 int y;
1614 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1615 y = -h + PAGE_SCROLL_OVERLAP;
1616 } else {
1617 y = -h / 2;
1618 }
1619 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001620 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 : extendScroll(y);
1622 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 /**
1625 * Scroll the contents of the view down by half the page size
1626 * @param bottom true to jump to bottom of page
1627 * @return true if the page was scrolled
1628 */
1629 public boolean pageDown(boolean bottom) {
1630 if (mNativeClass == 0) {
1631 return false;
1632 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001633 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001634 if (bottom) {
Grace Klobad7439f42009-11-10 14:11:33 -08001635 return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001636 }
1637 // Page down.
1638 int h = getHeight();
1639 int y;
1640 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1641 y = h - PAGE_SCROLL_OVERLAP;
1642 } else {
1643 y = h / 2;
1644 }
1645 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001646 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001647 : extendScroll(y);
1648 }
1649
1650 /**
1651 * Clear the view so that onDraw() will draw nothing but white background,
1652 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
1653 */
1654 public void clearView() {
1655 mContentWidth = 0;
1656 mContentHeight = 0;
1657 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
1658 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001659
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 /**
1661 * Return a new picture that captures the current display of the webview.
1662 * This is a copy of the display, and will be unaffected if the webview
1663 * later loads a different URL.
1664 *
1665 * @return a picture containing the current contents of the view. Note this
1666 * picture is of the entire document, and is not restricted to the
1667 * bounds of the view.
1668 */
1669 public Picture capturePicture() {
Cary Clarkd6982c92009-05-29 11:02:22 -04001670 if (null == mWebViewCore) return null; // check for out of memory tab
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001671 return mWebViewCore.copyContentPicture();
1672 }
1673
1674 /**
1675 * Return true if the browser is displaying a TextView for text input.
1676 */
1677 private boolean inEditingMode() {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001678 return mWebTextView != null && mWebTextView.getParent() != null
1679 && mWebTextView.hasFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001680 }
1681
1682 private void clearTextEntry() {
1683 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001684 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001685 }
1686 }
1687
Cary Clarkd6982c92009-05-29 11:02:22 -04001688 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001689 * Return the current scale of the WebView
1690 * @return The current scale.
1691 */
1692 public float getScale() {
1693 return mActualScale;
1694 }
1695
1696 /**
1697 * Set the initial scale for the WebView. 0 means default. If
1698 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
1699 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
1700 * WebView starts will this value as initial scale.
1701 *
1702 * @param scaleInPercent The initial scale in percent.
1703 */
1704 public void setInitialScale(int scaleInPercent) {
Grace Kloba16efce72009-11-10 15:49:03 -08001705 mInitialScaleInPercent = scaleInPercent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001706 }
1707
1708 /**
1709 * Invoke the graphical zoom picker widget for this WebView. This will
1710 * result in the zoom widget appearing on the screen to control the zoom
1711 * level of this WebView.
1712 */
1713 public void invokeZoomPicker() {
1714 if (!getSettings().supportZoom()) {
1715 Log.w(LOGTAG, "This WebView doesn't support zoom.");
1716 return;
1717 }
1718 clearTextEntry();
The Android Open Source Project10592532009-03-18 17:39:46 -07001719 if (getSettings().getBuiltInZoomControls()) {
1720 mZoomButtonsController.setVisible(true);
1721 } else {
1722 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
1723 mPrivateHandler.postDelayed(mZoomControlRunnable,
1724 ZOOM_CONTROLS_TIMEOUT);
1725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 }
1727
1728 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001729 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 * is found and the anchor has a non-javascript url, the HitTestResult type
1731 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
1732 * anchor does not have a url or if it is a javascript url, the type will
1733 * be UNKNOWN_TYPE and the url has to be retrieved through
1734 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
1735 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
1736 * the "extra" field. A type of
1737 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
1738 * a child node. If a phone number is found, the HitTestResult type is set
1739 * to PHONE_TYPE and the phone number is set in the "extra" field of
1740 * HitTestResult. If a map address is found, the HitTestResult type is set
1741 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
1742 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
1743 * and the email is set in the "extra" field of HitTestResult. Otherwise,
1744 * HitTestResult type is set to UNKNOWN_TYPE.
1745 */
1746 public HitTestResult getHitTestResult() {
1747 if (mNativeClass == 0) {
1748 return null;
1749 }
1750
1751 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04001752 if (nativeHasCursorNode()) {
1753 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001754 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04001755 } else {
1756 String text = nativeCursorText();
1757 if (text != null) {
1758 if (text.startsWith(SCHEME_TEL)) {
1759 result.setType(HitTestResult.PHONE_TYPE);
1760 result.setExtra(text.substring(SCHEME_TEL.length()));
1761 } else if (text.startsWith(SCHEME_MAILTO)) {
1762 result.setType(HitTestResult.EMAIL_TYPE);
1763 result.setExtra(text.substring(SCHEME_MAILTO.length()));
1764 } else if (text.startsWith(SCHEME_GEO)) {
1765 result.setType(HitTestResult.GEO_TYPE);
1766 result.setExtra(URLDecoder.decode(text
1767 .substring(SCHEME_GEO.length())));
1768 } else if (nativeCursorIsAnchor()) {
1769 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
1770 result.setExtra(text);
1771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001772 }
1773 }
1774 }
1775 int type = result.getType();
1776 if (type == HitTestResult.UNKNOWN_TYPE
1777 || type == HitTestResult.SRC_ANCHOR_TYPE) {
1778 // Now check to see if it is an image.
Leon Scroggins0236e672009-09-02 21:12:08 -04001779 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1780 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001781 String text = nativeImageURI(contentX, contentY);
1782 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001783 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
1784 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001785 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
1786 result.setExtra(text);
1787 }
1788 }
1789 return result;
1790 }
1791
Leon Scrogginse26efa32009-12-15 16:38:45 -05001792 // Called by JNI when the DOM has changed the focus. Clear the focus so
1793 // that new keys will go to the newly focused field
1794 private void domChangedFocus() {
1795 if (inEditingMode()) {
1796 mPrivateHandler.obtainMessage(DOM_FOCUS_CHANGED).sendToTarget();
1797 }
1798 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001799 /**
1800 * Request the href of an anchor element due to getFocusNodePath returning
1801 * "href." If hrefMsg is null, this method returns immediately and does not
1802 * dispatch hrefMsg to its target.
Cary Clarkd6982c92009-05-29 11:02:22 -04001803 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001804 * @param hrefMsg This message will be dispatched with the result of the
1805 * request as the data member with "url" as key. The result can
1806 * be null.
1807 */
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001808 // FIXME: API change required to change the name of this function. We now
1809 // look at the cursor node, and not the focus node. Also, what is
1810 // getFocusNodePath?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001811 public void requestFocusNodeHref(Message hrefMsg) {
1812 if (hrefMsg == null || mNativeClass == 0) {
1813 return;
1814 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001815 if (nativeCursorIsAnchor()) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001816 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
Cary Clarkd6982c92009-05-29 11:02:22 -04001817 nativeCursorFramePointer(), nativeCursorNodePointer(),
1818 hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001819 }
1820 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001821
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 /**
1823 * Request the url of the image last touched by the user. msg will be sent
1824 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04001825 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826 * @param msg This message will be dispatched with the result of the request
1827 * as the data member with "url" as key. The result can be null.
1828 */
1829 public void requestImageRef(Message msg) {
Cary Clark7f970112009-10-15 15:29:08 -04001830 if (0 == mNativeClass) return; // client isn't initialized
Leon Scroggins0236e672009-09-02 21:12:08 -04001831 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1832 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001833 String ref = nativeImageURI(contentX, contentY);
1834 Bundle data = msg.getData();
1835 data.putString("url", ref);
1836 msg.setData(data);
1837 msg.sendToTarget();
1838 }
1839
1840 private static int pinLoc(int x, int viewMax, int docMax) {
1841// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
1842 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001843 // pin the short document to the top/left of the screen
1844 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001845// Log.d(LOGTAG, "--- center " + x);
1846 } else if (x < 0) {
1847 x = 0;
1848// Log.d(LOGTAG, "--- zero");
1849 } else if (x + viewMax > docMax) {
1850 x = docMax - viewMax;
1851// Log.d(LOGTAG, "--- pin " + x);
1852 }
1853 return x;
1854 }
1855
1856 // Expects x in view coordinates
1857 private int pinLocX(int x) {
1858 return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
1859 }
1860
1861 // Expects y in view coordinates
1862 private int pinLocY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04001863 int titleH = getTitleHeight();
1864 // if the titlebar is still visible, just pin against 0
1865 if (y <= titleH) {
1866 return Math.max(y, 0);
1867 }
1868 // convert to 0-based coordinate (subtract the title height)
1869 // pin(), and then add the title height back in
1870 return pinLoc(y - titleH, getViewHeight(),
1871 computeVerticalScrollRange()) + titleH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001872 }
1873
Leon Scroggins0236e672009-09-02 21:12:08 -04001874 /**
1875 * A title bar which is embedded in this WebView, and scrolls along with it
1876 * vertically, but not horizontally.
1877 */
1878 private View mTitleBar;
1879
1880 /**
Leon Scroggins58992ea2009-09-17 15:55:31 -04001881 * Since we draw the title bar ourselves, we removed the shadow from the
1882 * browser's activity. We do want a shadow at the bottom of the title bar,
1883 * or at the top of the screen if the title bar is not visible. This
1884 * drawable serves that purpose.
1885 */
1886 private Drawable mTitleShadow;
1887
1888 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001889 * Add or remove a title bar to be embedded into the WebView, and scroll
1890 * along with it vertically, while remaining in view horizontally. Pass
1891 * null to remove the title bar from the WebView, and return to drawing
1892 * the WebView normally without translating to account for the title bar.
1893 * @hide
1894 */
Leon Scroggins078c52c2009-09-04 16:58:09 -04001895 public void setEmbeddedTitleBar(View v) {
1896 if (mTitleBar == v) return;
1897 if (mTitleBar != null) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001898 removeView(mTitleBar);
Leon Scroggins078c52c2009-09-04 16:58:09 -04001899 }
1900 if (null != v) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001901 addView(v, new AbsoluteLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001902 ViewGroup.LayoutParams.MATCH_PARENT,
Leon Scroggins078c52c2009-09-04 16:58:09 -04001903 ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
Leon Scroggins58992ea2009-09-17 15:55:31 -04001904 if (mTitleShadow == null) {
1905 mTitleShadow = (Drawable) mContext.getResources().getDrawable(
1906 com.android.internal.R.drawable.title_bar_shadow);
1907 }
Leon Scroggins0236e672009-09-02 21:12:08 -04001908 }
1909 mTitleBar = v;
1910 }
1911
1912 /**
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001913 * Given a distance in view space, convert it to content space. Note: this
1914 * does not reflect translation, just scaling, so this should not be called
1915 * with coordinates, but should be called for dimensions like width or
1916 * height.
1917 */
1918 private int viewToContentDimension(int d) {
1919 return Math.round(d * mInvActualScale);
1920 }
1921
1922 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001923 * Given an x coordinate in view space, convert it to content space. Also
1924 * may be used for absolute heights (such as for the WebTextView's
1925 * textSize, which is unaffected by the height of the title bar).
1926 */
1927 /*package*/ int viewToContentX(int x) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001928 return viewToContentDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001929 }
1930
Leon Scroggins0236e672009-09-02 21:12:08 -04001931 /**
1932 * Given a y coordinate in view space, convert it to content space.
1933 * Takes into account the height of the title bar if there is one
1934 * embedded into the WebView.
1935 */
1936 /*package*/ int viewToContentY(int y) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001937 return viewToContentDimension(y - getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04001938 }
1939
1940 /**
1941 * Given a distance in content space, convert it to view space. Note: this
1942 * does not reflect translation, just scaling, so this should not be called
1943 * with coordinates, but should be called for dimensions like width or
1944 * height.
1945 */
1946 /*package*/ int contentToViewDimension(int d) {
1947 return Math.round(d * mActualScale);
Leon Scroggins0236e672009-09-02 21:12:08 -04001948 }
1949
1950 /**
1951 * Given an x coordinate in content space, convert it to view
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001952 * space.
Leon Scroggins0236e672009-09-02 21:12:08 -04001953 */
1954 /*package*/ int contentToViewX(int x) {
Mike Reede8853fc2009-09-04 14:01:48 -04001955 return contentToViewDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001956 }
1957
Leon Scroggins0236e672009-09-02 21:12:08 -04001958 /**
1959 * Given a y coordinate in content space, convert it to view
1960 * space. Takes into account the height of the title bar.
1961 */
1962 /*package*/ int contentToViewY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04001963 return contentToViewDimension(y) + getTitleHeight();
Leon Scroggins0236e672009-09-02 21:12:08 -04001964 }
1965
Mike Reede9e86b82009-09-15 11:26:53 -04001966 private Rect contentToViewRect(Rect x) {
1967 return new Rect(contentToViewX(x.left), contentToViewY(x.top),
1968 contentToViewX(x.right), contentToViewY(x.bottom));
1969 }
1970
1971 /* To invalidate a rectangle in content coordinates, we need to transform
1972 the rect into view coordinates, so we can then call invalidate(...).
1973
1974 Normally, we would just call contentToView[XY](...), which eventually
1975 calls Math.round(coordinate * mActualScale). However, for invalidates,
1976 we need to account for the slop that occurs with antialiasing. To
1977 address that, we are a little more liberal in the size of the rect that
1978 we invalidate.
1979
1980 This liberal calculation calls floor() for the top/left, and ceil() for
1981 the bottom/right coordinates. This catches the possible extra pixels of
1982 antialiasing that we might have missed with just round().
1983 */
1984
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001985 // Called by JNI to invalidate the View, given rectangle coordinates in
1986 // content space
1987 private void viewInvalidate(int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04001988 final float scale = mActualScale;
1989 final int dy = getTitleHeight();
1990 invalidate((int)Math.floor(l * scale),
1991 (int)Math.floor(t * scale) + dy,
1992 (int)Math.ceil(r * scale),
1993 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001994 }
1995
1996 // Called by JNI to invalidate the View after a delay, given rectangle
1997 // coordinates in content space
1998 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04001999 final float scale = mActualScale;
2000 final int dy = getTitleHeight();
2001 postInvalidateDelayed(delay,
2002 (int)Math.floor(l * scale),
2003 (int)Math.floor(t * scale) + dy,
2004 (int)Math.ceil(r * scale),
2005 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002006 }
2007
Mike Reede9e86b82009-09-15 11:26:53 -04002008 private void invalidateContentRect(Rect r) {
2009 viewInvalidate(r.left, r.top, r.right, r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002010 }
2011
Cary Clark278ce052009-08-31 16:08:42 -04002012 // stop the scroll animation, and don't let a subsequent fling add
2013 // to the existing velocity
2014 private void abortAnimation() {
2015 mScroller.abortAnimation();
2016 mLastVelocity = 0;
2017 }
2018
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002019 /* call from webcoreview.draw(), so we're still executing in the UI thread
2020 */
2021 private void recordNewContentSize(int w, int h, boolean updateLayout) {
2022
2023 // premature data from webkit, ignore
2024 if ((w | h) == 0) {
2025 return;
2026 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 // don't abort a scroll animation if we didn't change anything
2029 if (mContentWidth != w || mContentHeight != h) {
2030 // record new dimensions
2031 mContentWidth = w;
2032 mContentHeight = h;
2033 // If history Picture is drawn, don't update scroll. They will be
2034 // updated when we get out of that mode.
2035 if (!mDrawHistory) {
2036 // repin our scroll, taking into account the new content size
2037 int oldX = mScrollX;
2038 int oldY = mScrollY;
2039 mScrollX = pinLocX(mScrollX);
2040 mScrollY = pinLocY(mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002041 if (oldX != mScrollX || oldY != mScrollY) {
2042 sendOurVisibleRect();
2043 }
Leon Scrogginsd84e7d52009-09-29 11:11:45 -04002044 if (!mScroller.isFinished()) {
2045 // We are in the middle of a scroll. Repin the final scroll
2046 // position.
2047 mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
2048 mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
2049 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002050 }
2051 }
2052 contentSizeChanged(updateLayout);
2053 }
2054
Grace Kloba3a0def22010-01-23 21:11:54 -08002055 private void setNewZoomScale(float scale, boolean updateTextWrapScale,
2056 boolean force) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002057 if (scale < mMinZoomScale) {
2058 scale = mMinZoomScale;
2059 } else if (scale > mMaxZoomScale) {
2060 scale = mMaxZoomScale;
2061 }
Grace Kloba3a0def22010-01-23 21:11:54 -08002062 if (updateTextWrapScale) {
2063 mTextWrapScale = scale;
2064 // reset mLastHeightSent to force VIEW_SIZE_CHANGED sent to WebKit
2065 mLastHeightSent = 0;
2066 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002067 if (scale != mActualScale || force) {
2068 if (mDrawHistory) {
2069 // If history Picture is drawn, don't update scroll. They will
2070 // be updated when we get out of that mode.
2071 if (scale != mActualScale && !mPreviewZoomOnly) {
2072 mCallbackProxy.onScaleChanged(mActualScale, scale);
2073 }
2074 mActualScale = scale;
2075 mInvActualScale = 1 / scale;
Grace Kloba3a0def22010-01-23 21:11:54 -08002076 sendViewSizeZoom();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002077 } else {
2078 // update our scroll so we don't appear to jump
2079 // i.e. keep the center of the doc in the center of the view
2080
2081 int oldX = mScrollX;
2082 int oldY = mScrollY;
2083 float ratio = scale * mInvActualScale; // old inverse
2084 float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
Grace Klobac0c03af2009-09-17 11:01:44 -07002085 float sy = ratio * oldY + (ratio - 1)
2086 * (mZoomCenterY - getTitleHeight());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002087
2088 // now update our new scale and inverse
2089 if (scale != mActualScale && !mPreviewZoomOnly) {
2090 mCallbackProxy.onScaleChanged(mActualScale, scale);
2091 }
2092 mActualScale = scale;
2093 mInvActualScale = 1 / scale;
2094
Patrick Scott0a5ce012009-07-02 08:56:10 -04002095 // Scale all the child views
2096 mViewManager.scaleAll();
2097
Cary Clarkd6982c92009-05-29 11:02:22 -04002098 // as we don't have animation for scaling, don't do animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002099 // for scrolling, as it causes weird intermediate state
2100 // pinScrollTo(Math.round(sx), Math.round(sy));
2101 mScrollX = pinLocX(Math.round(sx));
2102 mScrollY = pinLocY(Math.round(sy));
2103
Grace Kloba3a0def22010-01-23 21:11:54 -08002104 // update webkit
2105 sendViewSizeZoom();
2106 sendOurVisibleRect();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002107 }
2108 }
2109 }
2110
2111 // Used to avoid sending many visible rect messages.
2112 private Rect mLastVisibleRectSent;
2113 private Rect mLastGlobalRect;
2114
2115 private Rect sendOurVisibleRect() {
Grace Kloba3a0def22010-01-23 21:11:54 -08002116 if (mPreviewZoomOnly) return mLastVisibleRectSent;
2117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002118 Rect rect = new Rect();
2119 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002120 // Rect.equals() checks for null input.
2121 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04002122 Point pos = new Point(rect.left, rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002123 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Cary Clarked56eda2009-06-18 09:48:47 -04002124 nativeMoveGeneration(), 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002125 mLastVisibleRectSent = rect;
2126 }
2127 Rect globalRect = new Rect();
2128 if (getGlobalVisibleRect(globalRect)
2129 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04002130 if (DebugFlags.WEB_VIEW) {
2131 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
2132 + globalRect.top + ",r=" + globalRect.right + ",b="
2133 + globalRect.bottom);
2134 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002135 // TODO: the global offset is only used by windowRect()
2136 // in ChromeClientAndroid ; other clients such as touch
2137 // and mouse events could return view + screen relative points.
2138 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
2139 mLastGlobalRect = globalRect;
2140 }
2141 return rect;
2142 }
2143
2144 // Sets r to be the visible rectangle of our webview in view coordinates
2145 private void calcOurVisibleRect(Rect r) {
2146 Point p = new Point();
2147 getGlobalVisibleRect(r, p);
2148 r.offset(-p.x, -p.y);
Cary Clark3524be92009-06-22 13:09:11 -04002149 if (mFindIsUp) {
Cary Clark5bb6b522009-09-21 11:58:31 -04002150 r.bottom -= mFindHeight;
Cary Clark3524be92009-06-22 13:09:11 -04002151 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002152 }
2153
2154 // Sets r to be our visible rectangle in content coordinates
2155 private void calcOurContentVisibleRect(Rect r) {
2156 calcOurVisibleRect(r);
Leon Scroggins0236e672009-09-02 21:12:08 -04002157 r.left = viewToContentX(r.left);
Leon Scroggins37df6a82009-09-23 10:31:23 -04002158 // viewToContentY will remove the total height of the title bar. Add
2159 // the visible height back in to account for the fact that if the title
2160 // bar is partially visible, the part of the visible rect which is
2161 // displaying our content is displaced by that amount.
Grace Kloba8eff73f2009-09-24 09:34:32 -07002162 r.top = viewToContentY(r.top + getVisibleTitleHeight());
Leon Scroggins0236e672009-09-02 21:12:08 -04002163 r.right = viewToContentX(r.right);
Grace Kloba8eff73f2009-09-24 09:34:32 -07002164 r.bottom = viewToContentY(r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002165 }
2166
Grace Klobaef347ef2009-07-30 11:20:32 -07002167 static class ViewSizeData {
2168 int mWidth;
2169 int mHeight;
2170 int mTextWrapWidth;
Grace Kloba3a0def22010-01-23 21:11:54 -08002171 int mAnchorX;
2172 int mAnchorY;
Grace Klobaef347ef2009-07-30 11:20:32 -07002173 float mScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002174 boolean mIgnoreHeight;
Grace Klobaef347ef2009-07-30 11:20:32 -07002175 }
2176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002177 /**
2178 * Compute unzoomed width and height, and if they differ from the last
2179 * values we sent, send them to webkit (to be used has new viewport)
2180 *
2181 * @return true if new values were sent
2182 */
2183 private boolean sendViewSizeZoom() {
Grace Kloba3a0def22010-01-23 21:11:54 -08002184 if (mPreviewZoomOnly) return false;
2185
Grace Klobaef347ef2009-07-30 11:20:32 -07002186 int viewWidth = getViewWidth();
2187 int newWidth = Math.round(viewWidth * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002188 int newHeight = Math.round(getViewHeight() * mInvActualScale);
2189 /*
2190 * Because the native side may have already done a layout before the
2191 * View system was able to measure us, we have to send a height of 0 to
2192 * remove excess whitespace when we grow our width. This will trigger a
2193 * layout and a change in content size. This content size change will
2194 * mean that contentSizeChanged will either call this method directly or
2195 * indirectly from onSizeChanged.
2196 */
2197 if (newWidth > mLastWidthSent && mWrapContent) {
2198 newHeight = 0;
2199 }
2200 // Avoid sending another message if the dimensions have not changed.
2201 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
Grace Klobaef347ef2009-07-30 11:20:32 -07002202 ViewSizeData data = new ViewSizeData();
2203 data.mWidth = newWidth;
2204 data.mHeight = newHeight;
Grace Kloba3a0def22010-01-23 21:11:54 -08002205 data.mTextWrapWidth = Math.round(viewWidth / mTextWrapScale);;
Grace Klobaef347ef2009-07-30 11:20:32 -07002206 data.mScale = mActualScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002207 data.mIgnoreHeight = mZoomScale != 0 && !mHeightCanMeasure;
Grace Kloba3a0def22010-01-23 21:11:54 -08002208 data.mAnchorX = mAnchorX;
2209 data.mAnchorY = mAnchorY;
Grace Klobaef347ef2009-07-30 11:20:32 -07002210 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002211 mLastWidthSent = newWidth;
2212 mLastHeightSent = newHeight;
Grace Kloba3a0def22010-01-23 21:11:54 -08002213 mAnchorX = mAnchorY = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002214 return true;
2215 }
2216 return false;
2217 }
2218
2219 @Override
2220 protected int computeHorizontalScrollRange() {
2221 if (mDrawHistory) {
2222 return mHistoryWidth;
2223 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002224 // to avoid rounding error caused unnecessary scrollbar, use floor
2225 return (int) Math.floor(mContentWidth * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002226 }
2227 }
2228
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002229 @Override
2230 protected int computeVerticalScrollRange() {
2231 if (mDrawHistory) {
2232 return mHistoryHeight;
2233 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002234 // to avoid rounding error caused unnecessary scrollbar, use floor
2235 return (int) Math.floor(mContentHeight * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002236 }
2237 }
2238
Leon Scroggins0236e672009-09-02 21:12:08 -04002239 @Override
2240 protected int computeVerticalScrollOffset() {
Mike Reede8853fc2009-09-04 14:01:48 -04002241 return Math.max(mScrollY - getTitleHeight(), 0);
Leon Scroggins0236e672009-09-02 21:12:08 -04002242 }
2243
2244 @Override
2245 protected int computeVerticalScrollExtent() {
2246 return getViewHeight();
2247 }
2248
Mike Reede8853fc2009-09-04 14:01:48 -04002249 /** @hide */
2250 @Override
2251 protected void onDrawVerticalScrollBar(Canvas canvas,
2252 Drawable scrollBar,
2253 int l, int t, int r, int b) {
2254 scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
2255 scrollBar.draw(canvas);
2256 }
2257
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002258 /**
2259 * Get the url for the current page. This is not always the same as the url
2260 * passed to WebViewClient.onPageStarted because although the load for
2261 * that url has begun, the current page may not have changed.
2262 * @return The url for the current page.
2263 */
2264 public String getUrl() {
2265 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2266 return h != null ? h.getUrl() : null;
2267 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002268
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002269 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002270 * Get the original url for the current page. This is not always the same
2271 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002272 * load for that url has begun, the current page may not have changed.
2273 * Also, there may have been redirects resulting in a different url to that
2274 * originally requested.
2275 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002276 */
2277 public String getOriginalUrl() {
2278 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2279 return h != null ? h.getOriginalUrl() : null;
2280 }
2281
2282 /**
2283 * Get the title for the current page. This is the title of the current page
2284 * until WebViewClient.onReceivedTitle is called.
2285 * @return The title for the current page.
2286 */
2287 public String getTitle() {
2288 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2289 return h != null ? h.getTitle() : null;
2290 }
2291
2292 /**
2293 * Get the favicon for the current page. This is the favicon of the current
2294 * page until WebViewClient.onReceivedIcon is called.
2295 * @return The favicon for the current page.
2296 */
2297 public Bitmap getFavicon() {
2298 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2299 return h != null ? h.getFavicon() : null;
2300 }
2301
2302 /**
Patrick Scott2ba12622009-08-04 13:20:05 -04002303 * Get the touch icon url for the apple-touch-icon <link> element.
2304 * @hide
2305 */
2306 public String getTouchIconUrl() {
2307 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2308 return h != null ? h.getTouchIconUrl() : null;
2309 }
2310
2311 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002312 * Get the progress for the current page.
2313 * @return The progress for the current page between 0 and 100.
2314 */
2315 public int getProgress() {
2316 return mCallbackProxy.getProgress();
2317 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002318
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002319 /**
2320 * @return the height of the HTML content.
2321 */
2322 public int getContentHeight() {
2323 return mContentHeight;
2324 }
2325
2326 /**
Leon Scrogginsea96d1e2009-09-23 13:41:01 -04002327 * @return the width of the HTML content.
2328 * @hide
2329 */
2330 public int getContentWidth() {
2331 return mContentWidth;
2332 }
2333
2334 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002335 * Pause all layout, parsing, and javascript timers for all webviews. This
2336 * is a global requests, not restricted to just this webview. This can be
2337 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002338 */
2339 public void pauseTimers() {
2340 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
2341 }
2342
2343 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002344 * Resume all layout, parsing, and javascript timers for all webviews.
2345 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002346 */
2347 public void resumeTimers() {
2348 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
2349 }
2350
2351 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002352 * Call this to pause any extra processing associated with this view and
2353 * its associated DOM/plugins/javascript/etc. For example, if the view is
2354 * taken offscreen, this could be called to reduce unnecessary CPU and/or
2355 * network traffic. When the view is again "active", call onResume().
2356 *
2357 * Note that this differs from pauseTimers(), which affects all views/DOMs
2358 * @hide
2359 */
2360 public void onPause() {
2361 if (!mIsPaused) {
2362 mIsPaused = true;
2363 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
2364 }
2365 }
2366
2367 /**
2368 * Call this to balanace a previous call to onPause()
2369 * @hide
2370 */
2371 public void onResume() {
2372 if (mIsPaused) {
2373 mIsPaused = false;
2374 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2375 }
2376 }
2377
2378 /**
2379 * Returns true if the view is paused, meaning onPause() was called. Calling
2380 * onResume() sets the paused state back to false.
2381 * @hide
2382 */
2383 public boolean isPaused() {
2384 return mIsPaused;
2385 }
2386
2387 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002388 * Call this to inform the view that memory is low so that it can
2389 * free any available memory.
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002390 */
2391 public void freeMemory() {
2392 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2393 }
2394
2395 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002396 * Clear the resource cache. Note that the cache is per-application, so
2397 * this will clear the cache for all WebViews used.
2398 *
2399 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002400 */
2401 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002402 // Note: this really needs to be a static method as it clears cache for all
2403 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2404 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002405 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2406 includeDiskFiles ? 1 : 0, 0);
2407 }
2408
2409 /**
2410 * Make sure that clearing the form data removes the adapter from the
2411 * currently focused textfield if there is one.
2412 */
2413 public void clearFormData() {
2414 if (inEditingMode()) {
2415 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002416 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002417 }
2418 }
2419
2420 /**
2421 * Tell the WebView to clear its internal back/forward list.
2422 */
2423 public void clearHistory() {
2424 mCallbackProxy.getBackForwardList().setClearPending();
2425 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2426 }
2427
2428 /**
2429 * Clear the SSL preferences table stored in response to proceeding with SSL
2430 * certificate errors.
2431 */
2432 public void clearSslPreferences() {
2433 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2434 }
2435
2436 /**
2437 * Return the WebBackForwardList for this WebView. This contains the
2438 * back/forward list for use in querying each item in the history stack.
2439 * This is a copy of the private WebBackForwardList so it contains only a
2440 * snapshot of the current state. Multiple calls to this method may return
2441 * different objects. The object returned from this method will not be
2442 * updated to reflect any new state.
2443 */
2444 public WebBackForwardList copyBackForwardList() {
2445 return mCallbackProxy.getBackForwardList().clone();
2446 }
2447
2448 /*
2449 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04002450 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002451 * calling findAll.
2452 *
2453 * @param forward Direction to search.
2454 */
2455 public void findNext(boolean forward) {
Cary Clark7f970112009-10-15 15:29:08 -04002456 if (0 == mNativeClass) return; // client isn't initialized
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002457 nativeFindNext(forward);
2458 }
2459
2460 /*
2461 * Find all instances of find on the page and highlight them.
2462 * @param find String to find.
2463 * @return int The number of occurances of the String "find"
2464 * that were found.
2465 */
2466 public int findAll(String find) {
Cary Clark7f970112009-10-15 15:29:08 -04002467 if (0 == mNativeClass) return 0; // client isn't initialized
Cary Clark5bb6b522009-09-21 11:58:31 -04002468 if (mFindIsUp == false) {
2469 recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
2470 false);
2471 mFindIsUp = true;
2472 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002473 int result = nativeFindAll(find.toLowerCase(), find.toUpperCase());
2474 invalidate();
Leon Scroggins5de63892009-10-29 09:48:43 -04002475 mLastFind = find;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002476 return result;
2477 }
2478
2479 // Used to know whether the find dialog is open. Affects whether
2480 // or not we draw the highlights for matches.
2481 private boolean mFindIsUp;
Cary Clark5bb6b522009-09-21 11:58:31 -04002482 private int mFindHeight;
Leon Scroggins5de63892009-10-29 09:48:43 -04002483 // Keep track of the last string sent, so we can search again after an
2484 // orientation change or the dismissal of the soft keyboard.
2485 private String mLastFind;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002486
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002487 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002488 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002489 * location. Currently, only addresses in the United States are detected,
2490 * and consist of:
2491 * - a house number
2492 * - a street name
2493 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2494 * - a city name
2495 * - a state or territory, either spelled out or two-letter abbr.
2496 * - an optional 5 digit or 9 digit zip code.
2497 *
2498 * All names must be correctly capitalized, and the zip code, if present,
2499 * must be valid for the state. The street type must be a standard USPS
2500 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04002501 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002502 * five digits.
2503 * @param addr The string to search for addresses.
2504 *
2505 * @return the address, or if no address is found, return null.
2506 */
2507 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04002508 return findAddress(addr, false);
2509 }
2510
2511 /**
2512 * @hide
2513 * Return the first substring consisting of the address of a physical
2514 * location. Currently, only addresses in the United States are detected,
2515 * and consist of:
2516 * - a house number
2517 * - a street name
2518 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2519 * - a city name
2520 * - a state or territory, either spelled out or two-letter abbr.
2521 * - an optional 5 digit or 9 digit zip code.
2522 *
2523 * Names are optionally capitalized, and the zip code, if present,
2524 * must be valid for the state. The street type must be a standard USPS
2525 * spelling or abbreviation. The state or territory must also be spelled
2526 * or abbreviated using USPS standards. The house number may not exceed
2527 * five digits.
2528 * @param addr The string to search for addresses.
2529 * @param caseInsensitive addr Set to true to make search ignore case.
2530 *
2531 * @return the address, or if no address is found, return null.
2532 */
2533 public static String findAddress(String addr, boolean caseInsensitive) {
2534 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002535 }
2536
2537 /*
2538 * Clear the highlighting surrounding text matches created by findAll.
2539 */
2540 public void clearMatches() {
Cary Clark32847a92009-11-17 16:04:18 -05002541 if (mNativeClass == 0)
2542 return;
Cary Clark5bb6b522009-09-21 11:58:31 -04002543 if (mFindIsUp) {
2544 recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
2545 false);
2546 mFindIsUp = false;
2547 }
Cary Clark32847a92009-11-17 16:04:18 -05002548 nativeSetFindIsUp();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002549 // Now that the dialog has been removed, ensure that we scroll to a
2550 // location that is not beyond the end of the page.
2551 pinScrollTo(mScrollX, mScrollY, false, 0);
2552 invalidate();
2553 }
2554
2555 /**
Cary Clark5bb6b522009-09-21 11:58:31 -04002556 * @hide
2557 */
2558 public void setFindDialogHeight(int height) {
2559 if (DebugFlags.WEB_VIEW) {
2560 Log.v(LOGTAG, "setFindDialogHeight height=" + height);
2561 }
2562 mFindHeight = height;
2563 }
2564
2565 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002566 * Query the document to see if it contains any image references. The
2567 * message object will be dispatched with arg1 being set to 1 if images
2568 * were found and 0 if the document does not reference any images.
2569 * @param response The message that will be dispatched with the result.
2570 */
2571 public void documentHasImages(Message response) {
2572 if (response == null) {
2573 return;
2574 }
2575 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
2576 }
2577
2578 @Override
2579 public void computeScroll() {
2580 if (mScroller.computeScrollOffset()) {
2581 int oldX = mScrollX;
2582 int oldY = mScrollY;
2583 mScrollX = mScroller.getCurrX();
2584 mScrollY = mScroller.getCurrY();
2585 postInvalidate(); // So we draw again
2586 if (oldX != mScrollX || oldY != mScrollY) {
2587 // as onScrollChanged() is not called, sendOurVisibleRect()
2588 // needs to be call explicitly
2589 sendOurVisibleRect();
2590 }
2591 } else {
2592 super.computeScroll();
2593 }
2594 }
2595
2596 private static int computeDuration(int dx, int dy) {
2597 int distance = Math.max(Math.abs(dx), Math.abs(dy));
2598 int duration = distance * 1000 / STD_SPEED;
2599 return Math.min(duration, MAX_DURATION);
2600 }
2601
2602 // helper to pin the scrollBy parameters (already in view coordinates)
2603 // returns true if the scroll was changed
2604 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
2605 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
2606 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002607 // helper to pin the scrollTo parameters (already in view coordinates)
2608 // returns true if the scroll was changed
2609 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
2610 x = pinLocX(x);
2611 y = pinLocY(y);
2612 int dx = x - mScrollX;
2613 int dy = y - mScrollY;
2614
2615 if ((dx | dy) == 0) {
2616 return false;
2617 }
Leon Scrogginsd55de402009-09-17 14:19:49 -04002618 if (animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002619 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002620 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
2621 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
Mike Cleronf116bf82009-09-27 19:14:12 -07002622 awakenScrollBars(mScroller.getDuration());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002623 invalidate();
2624 } else {
Cary Clark278ce052009-08-31 16:08:42 -04002625 abortAnimation(); // just in case
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002626 scrollTo(x, y);
2627 }
2628 return true;
2629 }
2630
2631 // Scale from content to view coordinates, and pin.
2632 // Also called by jni webview.cpp
Leon Scroggins4c943042009-07-31 15:05:42 -04002633 private boolean setContentScrollBy(int cx, int cy, boolean animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002634 if (mDrawHistory) {
2635 // disallow WebView to change the scroll position as History Picture
2636 // is used in the view system.
2637 // TODO: as we switchOutDrawHistory when trackball or navigation
2638 // keys are hit, this should be safe. Right?
Leon Scroggins4c943042009-07-31 15:05:42 -04002639 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002640 }
Mike Reede8853fc2009-09-04 14:01:48 -04002641 cx = contentToViewDimension(cx);
2642 cy = contentToViewDimension(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002643 if (mHeightCanMeasure) {
2644 // move our visible rect according to scroll request
2645 if (cy != 0) {
2646 Rect tempRect = new Rect();
2647 calcOurVisibleRect(tempRect);
2648 tempRect.offset(cx, cy);
2649 requestRectangleOnScreen(tempRect);
2650 }
2651 // FIXME: We scroll horizontally no matter what because currently
2652 // ScrollView and ListView will not scroll horizontally.
2653 // FIXME: Why do we only scroll horizontally if there is no
2654 // vertical scroll?
2655// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
Leon Scroggins4c943042009-07-31 15:05:42 -04002656 return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002657 } else {
Leon Scroggins4c943042009-07-31 15:05:42 -04002658 return pinScrollBy(cx, cy, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002659 }
2660 }
2661
Leon Scroggins405d7852009-11-02 14:50:54 -08002662 /**
2663 * Called by CallbackProxy when the page finishes loading.
2664 * @param url The URL of the page which has finished loading.
2665 */
2666 /* package */ void onPageFinished(String url) {
2667 if (mPageThatNeedsToSlideTitleBarOffScreen != null) {
2668 // If the user is now on a different page, or has scrolled the page
2669 // past the point where the title bar is offscreen, ignore the
2670 // scroll request.
2671 if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url)
2672 && mScrollX == 0 && mScrollY == 0) {
2673 pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true,
2674 SLIDE_TITLE_DURATION);
2675 }
2676 mPageThatNeedsToSlideTitleBarOffScreen = null;
2677 }
2678 }
2679
2680 /**
2681 * The URL of a page that sent a message to scroll the title bar off screen.
2682 *
2683 * Many mobile sites tell the page to scroll to (0,1) in order to scroll the
2684 * title bar off the screen. Sometimes, the scroll position is set before
2685 * the page finishes loading. Rather than scrolling while the page is still
2686 * loading, keep track of the URL and new scroll position so we can perform
2687 * the scroll once the page finishes loading.
2688 */
2689 private String mPageThatNeedsToSlideTitleBarOffScreen;
2690
2691 /**
2692 * The destination Y scroll position to be used when the page finishes
2693 * loading. See mPageThatNeedsToSlideTitleBarOffScreen.
2694 */
2695 private int mYDistanceToSlideTitleOffScreen;
2696
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002697 // scale from content to view coordinates, and pin
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002698 // return true if pin caused the final x/y different than the request cx/cy,
2699 // and a future scroll may reach the request cx/cy after our size has
2700 // changed
2701 // return false if the view scroll to the exact position as it is requested,
2702 // where negative numbers are taken to mean 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002703 private boolean setContentScrollTo(int cx, int cy) {
2704 if (mDrawHistory) {
2705 // disallow WebView to change the scroll position as History Picture
2706 // is used in the view system.
2707 // One known case where this is called is that WebCore tries to
2708 // restore the scroll position. As history Picture already uses the
2709 // saved scroll position, it is ok to skip this.
2710 return false;
2711 }
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002712 int vx;
2713 int vy;
2714 if ((cx | cy) == 0) {
2715 // If the page is being scrolled to (0,0), do not add in the title
2716 // bar's height, and simply scroll to (0,0). (The only other work
2717 // in contentToView_ is to multiply, so this would not change 0.)
2718 vx = 0;
2719 vy = 0;
2720 } else {
2721 vx = contentToViewX(cx);
2722 vy = contentToViewY(cy);
2723 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002724// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
2725// vx + " " + vy + "]");
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002726 // Some mobile sites attempt to scroll the title bar off the page by
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002727 // scrolling to (0,1). If we are at the top left corner of the
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002728 // page, assume this is an attempt to scroll off the title bar, and
2729 // animate the title bar off screen slowly enough that the user can see
2730 // it.
Leon Scroggins405d7852009-11-02 14:50:54 -08002731 if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0
2732 && mTitleBar != null) {
2733 // FIXME: 100 should be defined somewhere as our max progress.
2734 if (getProgress() < 100) {
2735 // Wait to scroll the title bar off screen until the page has
2736 // finished loading. Keep track of the URL and the destination
2737 // Y position
2738 mPageThatNeedsToSlideTitleBarOffScreen = getUrl();
2739 mYDistanceToSlideTitleOffScreen = vy;
2740 } else {
2741 pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
2742 }
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002743 // Since we are animating, we have not yet reached the desired
2744 // scroll position. Do not return true to request another attempt
2745 return false;
2746 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002747 pinScrollTo(vx, vy, false, 0);
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002748 // If the request was to scroll to a negative coordinate, treat it as if
2749 // it was a request to scroll to 0
2750 if ((mScrollX != vx && cx >= 0) || (mScrollY != vy && cy >= 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002751 return true;
2752 } else {
2753 return false;
2754 }
2755 }
2756
2757 // scale from content to view coordinates, and pin
2758 private void spawnContentScrollTo(int cx, int cy) {
2759 if (mDrawHistory) {
2760 // disallow WebView to change the scroll position as History Picture
2761 // is used in the view system.
2762 return;
2763 }
Leon Scroggins0236e672009-09-02 21:12:08 -04002764 int vx = contentToViewX(cx);
2765 int vy = contentToViewY(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002766 pinScrollTo(vx, vy, true, 0);
2767 }
2768
2769 /**
2770 * These are from webkit, and are in content coordinate system (unzoomed)
2771 */
2772 private void contentSizeChanged(boolean updateLayout) {
2773 // suppress 0,0 since we usually see real dimensions soon after
2774 // this avoids drawing the prev content in a funny place. If we find a
2775 // way to consolidate these notifications, this check may become
2776 // obsolete
2777 if ((mContentWidth | mContentHeight) == 0) {
2778 return;
2779 }
2780
2781 if (mHeightCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002782 if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002783 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002784 requestLayout();
2785 }
2786 } else if (mWidthCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002787 if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002788 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002789 requestLayout();
2790 }
2791 } else {
2792 // If we don't request a layout, try to send our view size to the
2793 // native side to ensure that WebCore has the correct dimensions.
2794 sendViewSizeZoom();
2795 }
2796 }
2797
2798 /**
2799 * Set the WebViewClient that will receive various notifications and
2800 * requests. This will replace the current handler.
2801 * @param client An implementation of WebViewClient.
2802 */
2803 public void setWebViewClient(WebViewClient client) {
2804 mCallbackProxy.setWebViewClient(client);
2805 }
2806
2807 /**
Grace Kloba94ab3b62009-10-07 18:00:19 -07002808 * Gets the WebViewClient
2809 * @return the current WebViewClient instance.
2810 *
2811 *@hide pending API council approval.
2812 */
2813 public WebViewClient getWebViewClient() {
2814 return mCallbackProxy.getWebViewClient();
2815 }
2816
2817 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002818 * Register the interface to be used when content can not be handled by
2819 * the rendering engine, and should be downloaded instead. This will replace
2820 * the current handler.
2821 * @param listener An implementation of DownloadListener.
2822 */
2823 public void setDownloadListener(DownloadListener listener) {
2824 mCallbackProxy.setDownloadListener(listener);
2825 }
2826
2827 /**
2828 * Set the chrome handler. This is an implementation of WebChromeClient for
2829 * use in handling Javascript dialogs, favicons, titles, and the progress.
2830 * This will replace the current handler.
2831 * @param client An implementation of WebChromeClient.
2832 */
2833 public void setWebChromeClient(WebChromeClient client) {
2834 mCallbackProxy.setWebChromeClient(client);
2835 }
2836
2837 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01002838 * Gets the chrome handler.
2839 * @return the current WebChromeClient instance.
2840 *
2841 * @hide API council approval.
2842 */
2843 public WebChromeClient getWebChromeClient() {
2844 return mCallbackProxy.getWebChromeClient();
2845 }
2846
2847 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002848 * Set the Picture listener. This is an interface used to receive
2849 * notifications of a new Picture.
2850 * @param listener An implementation of WebView.PictureListener.
2851 */
2852 public void setPictureListener(PictureListener listener) {
2853 mPictureListener = listener;
2854 }
2855
2856 /**
2857 * {@hide}
2858 */
2859 /* FIXME: Debug only! Remove for SDK! */
2860 public void externalRepresentation(Message callback) {
2861 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
2862 }
2863
2864 /**
2865 * {@hide}
2866 */
2867 /* FIXME: Debug only! Remove for SDK! */
2868 public void documentAsText(Message callback) {
2869 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
2870 }
2871
2872 /**
2873 * Use this function to bind an object to Javascript so that the
2874 * methods can be accessed from Javascript.
2875 * <p><strong>IMPORTANT:</strong>
2876 * <ul>
2877 * <li> Using addJavascriptInterface() allows JavaScript to control your
2878 * application. This can be a very useful feature or a dangerous security
2879 * issue. When the HTML in the WebView is untrustworthy (for example, part
2880 * or all of the HTML is provided by some person or process), then an
2881 * attacker could inject HTML that will execute your code and possibly any
2882 * code of the attacker's choosing.<br>
2883 * Do not use addJavascriptInterface() unless all of the HTML in this
2884 * WebView was written by you.</li>
2885 * <li> The Java object that is bound runs in another thread and not in
2886 * the thread that it was constructed in.</li>
2887 * </ul></p>
2888 * @param obj The class instance to bind to Javascript
2889 * @param interfaceName The name to used to expose the class in Javascript
2890 */
2891 public void addJavascriptInterface(Object obj, String interfaceName) {
Cary Clarkded054c2009-06-15 10:26:08 -04002892 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
2893 arg.mObject = obj;
2894 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002895 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
2896 }
2897
2898 /**
2899 * Return the WebSettings object used to control the settings for this
2900 * WebView.
2901 * @return A WebSettings object that can be used to control this WebView's
2902 * settings.
2903 */
2904 public WebSettings getSettings() {
2905 return mWebViewCore.getSettings();
2906 }
2907
2908 /**
2909 * Return the list of currently loaded plugins.
2910 * @return The list of currently loaded plugins.
Andrei Popescu385df692009-08-13 11:59:57 +01002911 *
2912 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002913 */
Andrei Popescu385df692009-08-13 11:59:57 +01002914 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002915 public static synchronized PluginList getPluginList() {
Grace Klobabb245ea2009-11-10 13:13:24 -08002916 return new PluginList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002917 }
2918
2919 /**
Andrei Popescu385df692009-08-13 11:59:57 +01002920 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002921 */
Andrei Popescu385df692009-08-13 11:59:57 +01002922 @Deprecated
2923 public void refreshPlugins(boolean reloadOpenPages) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002924
2925 //-------------------------------------------------------------------------
2926 // Override View methods
2927 //-------------------------------------------------------------------------
2928
2929 @Override
2930 protected void finalize() throws Throwable {
Cary Clark9a4c0632009-08-10 16:40:08 -04002931 try {
2932 destroy();
2933 } finally {
2934 super.finalize();
2935 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002936 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002937
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002938 @Override
Leon Scroggins0236e672009-09-02 21:12:08 -04002939 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
2940 if (child == mTitleBar) {
2941 // When drawing the title bar, move it horizontally to always show
2942 // at the top of the WebView.
2943 mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
2944 }
2945 return super.drawChild(canvas, child, drawingTime);
2946 }
2947
Mike Reed19f3f0e2009-11-12 12:50:20 -05002948 private void drawContent(Canvas canvas) {
Grace Kloba04b28682009-09-14 14:38:37 -07002949 // Update the buttons in the picture, so when we draw the picture
2950 // to the screen, they are in the correct state.
2951 // Tell the native side if user is a) touching the screen,
2952 // b) pressing the trackball down, or c) pressing the enter key
2953 // If the cursor is on a button, we need to draw it in the pressed
2954 // state.
2955 // If mNativeClass is 0, we should not reach here, so we do not
2956 // need to check it again.
2957 nativeRecordButtons(hasFocus() && hasWindowFocus(),
Mike Reed19f3f0e2009-11-12 12:50:20 -05002958 mTouchMode == TOUCH_SHORTPRESS_START_MODE
2959 || mTrackballDown || mGotCenterDown, false);
Grace Kloba04b28682009-09-14 14:38:37 -07002960 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
Mike Reed19f3f0e2009-11-12 12:50:20 -05002961 }
2962
2963 @Override
2964 protected void onDraw(Canvas canvas) {
2965 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
2966 if (mNativeClass == 0) {
2967 return;
2968 }
2969
2970 int saveCount = canvas.save();
2971 if (mTitleBar != null) {
2972 canvas.translate(0, (int) mTitleBar.getHeight());
2973 }
2974 if (mDragTrackerHandler == null || !mDragTrackerHandler.draw(canvas)) {
2975 drawContent(canvas);
2976 }
Leon Scroggins0236e672009-09-02 21:12:08 -04002977 canvas.restoreToCount(saveCount);
Cary Clarkd6982c92009-05-29 11:02:22 -04002978
Leon Scroggins58992ea2009-09-17 15:55:31 -04002979 // Now draw the shadow.
2980 if (mTitleBar != null) {
2981 int y = mScrollY + getVisibleTitleHeight();
2982 int height = (int) (5f * getContext().getResources()
2983 .getDisplayMetrics().density);
2984 mTitleShadow.setBounds(mScrollX, y, mScrollX + getWidth(),
2985 y + height);
2986 mTitleShadow.draw(canvas);
2987 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002988 if (AUTO_REDRAW_HACK && mAutoRedraw) {
2989 invalidate();
2990 }
Nicolas Roard38863332010-01-04 19:30:55 +00002991 mWebViewCore.signalRepaintDone();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002992 }
2993
2994 @Override
2995 public void setLayoutParams(ViewGroup.LayoutParams params) {
2996 if (params.height == LayoutParams.WRAP_CONTENT) {
2997 mWrapContent = true;
2998 }
2999 super.setLayoutParams(params);
3000 }
3001
3002 @Override
3003 public boolean performLongClick() {
Grace Kloba98e6fcf2010-01-27 15:20:30 -08003004 // performLongClick() is the result of a delayed message. If we switch
3005 // to windows overview, the WebView will be temporarily removed from the
3006 // view system. In that case, do nothing.
3007 if (getParent() == null) return false;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003008 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
3009 // Send the click so that the textfield is in focus
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003010 centerKeyPressOnTextField();
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003011 rebuildWebTextView();
3012 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003013 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003014 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003015 } else {
3016 return super.performLongClick();
3017 }
3018 }
3019
Grace Kloba94c715d2009-09-28 23:23:53 -07003020 boolean inAnimateZoom() {
3021 return mZoomScale != 0;
3022 }
3023
Leon Scroggins608f9f42009-09-03 10:06:04 -04003024 /**
3025 * Need to adjust the WebTextView after a change in zoom, since mActualScale
3026 * has changed. This is especially important for password fields, which are
3027 * drawn by the WebTextView, since it conveys more information than what
3028 * webkit draws. Thus we need to reposition it to show in the correct
3029 * place.
3030 */
3031 private boolean mNeedToAdjustWebTextView;
3032
Cary Clark5da9aeb2009-10-06 17:40:53 -04003033 private boolean didUpdateTextViewBounds(boolean allowIntersect) {
3034 Rect contentBounds = nativeFocusCandidateNodeBounds();
3035 Rect vBox = contentToViewRect(contentBounds);
3036 Rect visibleRect = new Rect();
3037 calcOurVisibleRect(visibleRect);
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003038 // The IME may have shown, resulting in the textfield being offscreen.
3039 // If so, the textfield will be scrolled on screen, so treat it as
3040 // though it is on screen. If it is on screen, place the WebTextView in
3041 // its new place, accounting for our new scroll/zoom values.
3042 InputMethodManager imm = InputMethodManager.peekInstance();
3043 if ((imm != null && imm.isActive(mWebTextView))
3044 || (allowIntersect ? Rect.intersects(visibleRect, vBox)
3045 : visibleRect.contains(vBox))) {
Cary Clark5da9aeb2009-10-06 17:40:53 -04003046 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
3047 vBox.height());
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003048 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
3049 contentToViewDimension(
3050 nativeFocusCandidateTextSize()));
Cary Clark5da9aeb2009-10-06 17:40:53 -04003051 return true;
3052 } else {
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003053 // The textfield is now off screen. The user probably
3054 // was not zooming to see the textfield better. Remove
3055 // the WebTextView. If the user types a key, and the
3056 // textfield is still in focus, we will reconstruct
3057 // the WebTextView and scroll it back on screen.
3058 mWebTextView.remove();
Cary Clark5da9aeb2009-10-06 17:40:53 -04003059 return false;
3060 }
3061 }
3062
Nicolas Roard38863332010-01-04 19:30:55 +00003063 private void drawLayers(Canvas canvas) {
3064 if (mRootLayer != 0) {
Nicolas Roardaf2af4e52010-01-11 13:20:16 +00003065 int scrollY = computeVerticalScrollOffset();
3066 int viewHeight = getHeight() - getVisibleTitleHeight();
3067
Nicolas Roard38863332010-01-04 19:30:55 +00003068 nativeDrawLayers(mRootLayer, mScrollX, scrollY,
Nicolas Roardaf2af4e52010-01-11 13:20:16 +00003069 getWidth(), viewHeight,
Nicolas Roard38863332010-01-04 19:30:55 +00003070 mActualScale, canvas);
3071 }
3072 }
3073
Cary Clarkd6982c92009-05-29 11:02:22 -04003074 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003075 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003076 if (mDrawHistory) {
3077 canvas.scale(mActualScale, mActualScale);
3078 canvas.drawPicture(mHistoryPicture);
Nicolas Roard38863332010-01-04 19:30:55 +00003079 drawLayers(canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003080 return;
3081 }
3082
3083 boolean animateZoom = mZoomScale != 0;
Cary Clark25415e22009-10-12 13:41:28 -04003084 boolean animateScroll = (!mScroller.isFinished()
3085 || mVelocityTracker != null)
3086 && (mTouchMode != TOUCH_DRAG_MODE ||
3087 mHeldMotionless != MOTIONLESS_TRUE);
3088 if (mTouchMode == TOUCH_DRAG_MODE) {
3089 if (mHeldMotionless == MOTIONLESS_PENDING) {
3090 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
3091 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
3092 mHeldMotionless = MOTIONLESS_FALSE;
3093 }
3094 if (mHeldMotionless == MOTIONLESS_FALSE) {
3095 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3096 .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
3097 mHeldMotionless = MOTIONLESS_PENDING;
3098 }
3099 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003100 if (animateZoom) {
3101 float zoomScale;
3102 int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
3103 if (interval < ZOOM_ANIMATION_LENGTH) {
3104 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
Cary Clarkd6982c92009-05-29 11:02:22 -04003105 zoomScale = 1.0f / (mInvInitialZoomScale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003106 + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
3107 invalidate();
3108 } else {
3109 zoomScale = mZoomScale;
3110 // set mZoomScale to be 0 as we have done animation
3111 mZoomScale = 0;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08003112 WebViewCore.resumeUpdatePicture(mWebViewCore);
Grace Klobadfe095a2009-09-17 07:56:48 -07003113 // call invalidate() again to draw with the final filters
3114 invalidate();
Leon Scroggins608f9f42009-09-03 10:06:04 -04003115 if (mNeedToAdjustWebTextView) {
3116 mNeedToAdjustWebTextView = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003117 if (didUpdateTextViewBounds(false)
3118 && nativeFocusCandidateIsPassword()) {
Leon Scroggins10be7542009-09-30 18:01:38 -04003119 // If it is a password field, start drawing the
3120 // WebTextView once again.
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003121 mWebTextView.setInPassword(true);
Leon Scroggins608f9f42009-09-03 10:06:04 -04003122 }
3123 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003124 }
Grace Klobac0c03af2009-09-17 11:01:44 -07003125 // calculate the intermediate scroll position. As we need to use
3126 // zoomScale, we can't use pinLocX/Y directly. Copy the logic here.
Grace Kloba675c7d22009-07-23 09:21:21 -07003127 float scale = zoomScale * mInvInitialZoomScale;
3128 int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
3129 - mZoomCenterX);
3130 tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
3131 * zoomScale)) + mScrollX;
Grace Klobac0c03af2009-09-17 11:01:44 -07003132 int titleHeight = getTitleHeight();
3133 int ty = Math.round(scale
3134 * (mInitialScrollY + mZoomCenterY - titleHeight)
3135 - (mZoomCenterY - titleHeight));
3136 ty = -(ty <= titleHeight ? Math.max(ty, 0) : pinLoc(ty
3137 - titleHeight, getViewHeight(), Math.round(mContentHeight
3138 * zoomScale)) + titleHeight) + mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003139 canvas.translate(tx, ty);
3140 canvas.scale(zoomScale, zoomScale);
Leon Scroggins608f9f42009-09-03 10:06:04 -04003141 if (inEditingMode() && !mNeedToAdjustWebTextView
3142 && mZoomScale != 0) {
3143 // The WebTextView is up. Keep track of this so we can adjust
3144 // its size and placement when we finish zooming
3145 mNeedToAdjustWebTextView = true;
3146 // If it is in password mode, turn it off so it does not draw
3147 // misplaced.
3148 if (nativeFocusCandidateIsPassword()) {
3149 mWebTextView.setInPassword(false);
3150 }
3151 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003152 } else {
3153 canvas.scale(mActualScale, mActualScale);
3154 }
3155
Grace Kloba3a0def22010-01-23 21:11:54 -08003156 mWebViewCore.drawContentPicture(canvas, color,
3157 (animateZoom || mPreviewZoomOnly), animateScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003158
Nicolas Roard38863332010-01-04 19:30:55 +00003159 drawLayers(canvas);
3160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003161 if (mNativeClass == 0) return;
Grace Kloba3a0def22010-01-23 21:11:54 -08003162 if (mShiftIsPressed && !(animateZoom || mPreviewZoomOnly)) {
Cary Clark09e383c2009-10-26 16:43:58 -04003163 if (mTouchSelection || mExtendSelection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003164 nativeDrawSelectionRegion(canvas);
Cary Clark09e383c2009-10-26 16:43:58 -04003165 }
3166 if (!mTouchSelection) {
3167 nativeDrawSelectionPointer(canvas, mInvActualScale, mSelectX,
3168 mSelectY - getTitleHeight(), mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003169 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003170 } else if (drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003171 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3172 mTouchMode = TOUCH_SHORTPRESS_MODE;
3173 HitTestResult hitTest = getHitTestResult();
Grace Kloba5f68d6f2009-12-08 18:42:54 -08003174 if (mPreventLongPress || (hitTest != null &&
3175 hitTest.mType != HitTestResult.UNKNOWN_TYPE)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003176 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3177 .obtainMessage(SWITCH_TO_LONGPRESS),
3178 LONG_PRESS_TIMEOUT);
3179 }
3180 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003181 nativeDrawCursorRing(canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003182 }
3183 // When the FindDialog is up, only draw the matches if we are not in
3184 // the process of scrolling them into view.
3185 if (mFindIsUp && !animateScroll) {
3186 nativeDrawMatches(canvas);
3187 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04003188 if (mFocusSizeChanged) {
3189 mFocusSizeChanged = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003190 // If we are zooming, this will get handled above, when the zoom
3191 // finishes. We also do not need to do this unless the WebTextView
3192 // is showing.
3193 if (!animateZoom && inEditingMode()) {
3194 didUpdateTextViewBounds(true);
3195 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04003196 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003197 }
3198
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003199 // draw history
3200 private boolean mDrawHistory = false;
3201 private Picture mHistoryPicture = null;
3202 private int mHistoryWidth = 0;
3203 private int mHistoryHeight = 0;
3204
3205 // Only check the flag, can be called from WebCore thread
3206 boolean drawHistory() {
3207 return mDrawHistory;
3208 }
3209
3210 // Should only be called in UI thread
3211 void switchOutDrawHistory() {
3212 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Cary Clarkbc2e33b2009-04-23 13:12:19 -04003213 if (mDrawHistory && mWebViewCore.pictureReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003214 mDrawHistory = false;
3215 invalidate();
3216 int oldScrollX = mScrollX;
3217 int oldScrollY = mScrollY;
3218 mScrollX = pinLocX(mScrollX);
3219 mScrollY = pinLocY(mScrollY);
3220 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
3221 mUserScroll = false;
3222 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
3223 oldScrollY);
3224 }
3225 sendOurVisibleRect();
3226 }
3227 }
3228
Cary Clarkd6982c92009-05-29 11:02:22 -04003229 WebViewCore.CursorData cursorData() {
3230 WebViewCore.CursorData result = new WebViewCore.CursorData();
3231 result.mMoveGeneration = nativeMoveGeneration();
3232 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04003233 Point position = nativeCursorPosition();
3234 result.mX = position.x;
3235 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04003236 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003237 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003238
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003239 /**
3240 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04003241 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003242 * order, swap them.
3243 * @param start Beginning of selection to delete.
3244 * @param end End of selection to delete.
3245 */
3246 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003247 mTextGeneration++;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04003248 WebViewCore.TextSelectionData data
3249 = new WebViewCore.TextSelectionData(start, end);
3250 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
3251 data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003252 }
3253
3254 /**
3255 * Set the selection to (start, end) in the focused textfield. If start and
3256 * end are out of order, swap them.
3257 * @param start Beginning of selection.
3258 * @param end End of selection.
3259 */
3260 /* package */ void setSelection(int start, int end) {
Cary Clark2f1d60c2009-06-03 08:05:53 -04003261 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003262 }
3263
Derek Sollenberger7cabb032010-01-21 10:37:38 -05003264 @Override
3265 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
3266 InputConnection connection = super.onCreateInputConnection(outAttrs);
3267 outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN;
3268 return connection;
3269 }
3270
Leon Scroggins04e0a102010-01-08 16:19:27 -05003271 /**
3272 * Called in response to a message from webkit telling us that the soft
3273 * keyboard should be launched.
3274 */
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003275 private void displaySoftKeyboard(boolean isTextView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003276 InputMethodManager imm = (InputMethodManager)
3277 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003278
3279 if (isTextView) {
Leon Scroggins04e0a102010-01-08 16:19:27 -05003280 rebuildWebTextView();
3281 if (!inEditingMode()) return;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003282 imm.showSoftInput(mWebTextView, 0);
Grace Kloba3a0def22010-01-23 21:11:54 -08003283 // bring it back to the default scale so that user can enter text
3284 if (mActualScale < mDefaultScale) {
3285 mInZoomOverview = false;
3286 mZoomCenterX = mLastTouchX;
3287 mZoomCenterY = mLastTouchY;
3288 // do not change text wrap scale so that there is no reflow
3289 setNewZoomScale(mDefaultScale, false, false);
3290 didUpdateTextViewBounds(true);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003291 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003292 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003293 else { // used by plugins
3294 imm.showSoftInput(this, 0);
3295 }
3296 }
3297
3298 // Called by WebKit to instruct the UI to hide the keyboard
3299 private void hideSoftKeyboard() {
3300 InputMethodManager imm = (InputMethodManager)
3301 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
3302
3303 imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003304 }
3305
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003306 /*
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003307 * This method checks the current focus and cursor and potentially rebuilds
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003308 * mWebTextView to have the appropriate properties, such as password,
3309 * multiline, and what text it contains. It also removes it if necessary.
3310 */
Leon Scroggins01058282009-07-30 16:33:56 -04003311 /* package */ void rebuildWebTextView() {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003312 // If the WebView does not have focus, do nothing until it gains focus.
Grace Kloba04b28682009-09-14 14:38:37 -07003313 if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003314 return;
3315 }
3316 boolean alreadyThere = inEditingMode();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003317 // inEditingMode can only return true if mWebTextView is non-null,
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003318 // so we can safely call remove() if (alreadyThere)
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003319 if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003320 if (alreadyThere) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003321 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003322 }
3323 return;
3324 }
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003325 // At this point, we know we have found an input field, so go ahead
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003326 // and create the WebTextView if necessary.
3327 if (mWebTextView == null) {
3328 mWebTextView = new WebTextView(mContext, WebView.this);
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003329 // Initialize our generation number.
3330 mTextGeneration = 0;
3331 }
Leon Scroggins3a6c88c2009-09-09 14:50:23 -04003332 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
3333 contentToViewDimension(nativeFocusCandidateTextSize()));
Cary Clark3524be92009-06-22 13:09:11 -04003334 Rect visibleRect = new Rect();
3335 calcOurContentVisibleRect(visibleRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003336 // Note that sendOurVisibleRect calls viewToContent, so the coordinates
3337 // should be in content coordinates.
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003338 Rect bounds = nativeFocusCandidateNodeBounds();
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003339 Rect vBox = contentToViewRect(bounds);
3340 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height());
Cary Clarkd6982c92009-05-29 11:02:22 -04003341 if (!Rect.intersects(bounds, visibleRect)) {
Leon Scroggins40981262009-07-01 10:57:47 -04003342 mWebTextView.bringIntoView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003343 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003344 String text = nativeFocusCandidateText();
3345 int nodePointer = nativeFocusCandidatePointer();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003346 if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003347 // It is possible that we have the same textfield, but it has moved,
3348 // i.e. In the case of opening/closing the screen.
3349 // In that case, we need to set the dimensions, but not the other
3350 // aspects.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003351 // If the text has been changed by webkit, update it. However, if
3352 // there has been more UI text input, ignore it. We will receive
3353 // another update when that text is recognized.
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003354 if (text != null && !text.equals(mWebTextView.getText().toString())
Cary Clarkd6982c92009-05-29 11:02:22 -04003355 && nativeTextGeneration() == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003356 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003357 }
3358 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003359 mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
3360 Gravity.RIGHT : Gravity.NO_GRAVITY);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003361 // This needs to be called before setType, which may call
3362 // requestFormData, and it needs to have the correct nodePointer.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003363 mWebTextView.setNodePointer(nodePointer);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003364 mWebTextView.setType(nativeFocusCandidateType());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003365 if (null == text) {
Cary Clark243ea062009-06-25 10:49:32 -04003366 if (DebugFlags.WEB_VIEW) {
3367 Log.v(LOGTAG, "rebuildWebTextView null == text");
3368 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003369 text = "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003370 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003371 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003372 }
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003373 mWebTextView.requestFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003374 }
3375
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003376 /**
3377 * Called by WebTextView to find saved form data associated with the
3378 * textfield
3379 * @param name Name of the textfield.
3380 * @param nodePointer Pointer to the node of the textfield, so it can be
3381 * compared to the currently focused textfield when the data is
3382 * retrieved.
3383 */
3384 /* package */ void requestFormData(String name, int nodePointer) {
3385 if (mWebViewCore.getSettings().getSaveFormData()) {
3386 Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
3387 update.arg1 = nodePointer;
3388 RequestFormData updater = new RequestFormData(name, getUrl(),
3389 update);
3390 Thread t = new Thread(updater);
3391 t.start();
3392 }
3393 }
3394
Leon Scroggins3a503392010-01-06 17:04:38 -05003395 /**
3396 * Pass a message to find out the <label> associated with the <input>
3397 * identified by nodePointer
3398 * @param framePointer Pointer to the frame containing the <input> node
3399 * @param nodePointer Pointer to the node for which a <label> is desired.
3400 */
3401 /* package */ void requestLabel(int framePointer, int nodePointer) {
3402 mWebViewCore.sendMessage(EventHub.REQUEST_LABEL, framePointer,
3403 nodePointer);
3404 }
3405
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003406 /*
Nicolas Roard38863332010-01-04 19:30:55 +00003407 * This class runs the layers animations in their own thread,
3408 * so that we do not slow down the UI.
3409 */
3410 private class EvaluateLayersAnimations extends Thread {
3411 boolean mRunning = true;
3412 // delay corresponds to 40fps, no need to go faster.
3413 int mDelay = 25; // in ms
3414 public void run() {
3415 while (mRunning) {
3416 if (mLayersHaveAnimations && mRootLayer != 0) {
3417 // updates is a C++ pointer to a Vector of AnimationValues
3418 int updates = nativeEvaluateLayersAnimations(mRootLayer);
3419 if (updates == 0) {
3420 mRunning = false;
3421 }
3422 Message.obtain(mPrivateHandler,
3423 WebView.IMMEDIATE_REPAINT_MSG_ID,
3424 updates, 0).sendToTarget();
3425 } else {
3426 mRunning = false;
3427 }
3428 try {
3429 Thread.currentThread().sleep(mDelay);
3430 } catch (InterruptedException e) {
3431 mRunning = false;
3432 }
3433 }
3434 }
3435 public void cancel() {
3436 mRunning = false;
3437 }
3438 }
3439
3440 /*
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003441 * This class requests an Adapter for the WebTextView which shows past
3442 * entries stored in the database. It is a Runnable so that it can be done
3443 * in its own thread, without slowing down the UI.
3444 */
3445 private class RequestFormData implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003446 private String mName;
3447 private String mUrl;
3448 private Message mUpdateMessage;
3449
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003450 public RequestFormData(String name, String url, Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003451 mName = name;
3452 mUrl = url;
3453 mUpdateMessage = msg;
3454 }
3455
3456 public void run() {
3457 ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
3458 if (pastEntries.size() > 0) {
3459 AutoCompleteAdapter adapter = new
3460 AutoCompleteAdapter(mContext, pastEntries);
Cary Clarkded054c2009-06-15 10:26:08 -04003461 mUpdateMessage.obj = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003462 mUpdateMessage.sendToTarget();
3463 }
3464 }
3465 }
3466
Grace Kloba8ae1b412009-11-23 10:35:34 -08003467 /**
3468 * Dump the display tree to "/sdcard/displayTree.txt"
3469 *
3470 * @hide debug only
3471 */
3472 public void dumpDisplayTree() {
3473 nativeDumpDisplayTree(getUrl());
3474 }
3475
3476 /**
3477 * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
3478 * "/sdcard/domTree.txt"
3479 *
3480 * @hide debug only
3481 */
3482 public void dumpDomTree(boolean toFile) {
3483 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
3484 }
3485
3486 /**
3487 * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
3488 * to "/sdcard/renderTree.txt"
3489 *
3490 * @hide debug only
3491 */
3492 public void dumpRenderTree(boolean toFile) {
3493 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
3494 }
3495
Leon Scrogginse3225672009-06-03 15:53:13 -04003496 // This is used to determine long press with the center key. Does not
3497 // affect long press with the trackball/touch.
3498 private boolean mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003499
3500 @Override
3501 public boolean onKeyDown(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003502 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003503 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003504 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003505 }
3506
3507 if (mNativeClass == 0) {
3508 return false;
3509 }
3510
3511 // do this hack up front, so it always works, regardless of touch-mode
3512 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
3513 mAutoRedraw = !mAutoRedraw;
3514 if (mAutoRedraw) {
3515 invalidate();
3516 }
3517 return true;
3518 }
3519
3520 // Bubble up the key event if
3521 // 1. it is a system key; or
Grace Kloba04b28682009-09-14 14:38:37 -07003522 // 2. the host application wants to handle it;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003523 if (event.isSystem()
Grace Kloba04b28682009-09-14 14:38:37 -07003524 || mCallbackProxy.uiOverrideKeyEvent(event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003525 return false;
3526 }
3527
Cary Clark2f1d60c2009-06-03 08:05:53 -04003528 if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
Cary Clarkd6982c92009-05-29 11:02:22 -04003529 && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003530 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
Cary Clark09e383c2009-10-26 16:43:58 -04003531 setUpSelectXY();
3532 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003533
3534 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3535 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3536 // always handle the navigation keys in the UI thread
3537 switchOutDrawHistory();
Cary Clarkc05af372009-10-16 10:52:27 -04003538 if (mShiftIsPressed) {
3539 int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
3540 ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
3541 int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
3542 -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0;
3543 int multiplier = event.getRepeatCount() + 1;
3544 moveSelection(xRate * multiplier, yRate * multiplier);
3545 return true;
3546 }
Cary Clark215b72c2009-06-26 14:38:43 -04003547 if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003548 playSoundEffect(keyCodeToSoundsEffect(keyCode));
3549 return true;
3550 }
3551 // Bubble up the key event as WebView doesn't handle it
3552 return false;
3553 }
3554
Leon Scrogginse3225672009-06-03 15:53:13 -04003555 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003556 switchOutDrawHistory();
3557 if (event.getRepeatCount() == 0) {
Cary Clarkc05af372009-10-16 10:52:27 -04003558 if (mShiftIsPressed) {
3559 return true; // discard press if copy in progress
3560 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003561 mGotCenterDown = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003562 mPrivateHandler.sendMessageDelayed(mPrivateHandler
Leon Scrogginse3225672009-06-03 15:53:13 -04003563 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003564 // Already checked mNativeClass, so we do not need to check it
3565 // again.
3566 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
3567 return true;
3568 }
3569 // Bubble up the key event as WebView doesn't handle it
3570 return false;
3571 }
3572
Cary Clark843bbb82009-04-20 16:03:31 -04003573 if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
3574 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
3575 // turn off copy select if a shift-key combo is pressed
3576 mExtendSelection = mShiftIsPressed = false;
3577 if (mTouchMode == TOUCH_SELECT_MODE) {
3578 mTouchMode = TOUCH_INIT_MODE;
3579 }
3580 }
3581
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003582 if (getSettings().getNavDump()) {
3583 switch (keyCode) {
3584 case KeyEvent.KEYCODE_4:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003585 dumpDisplayTree();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003586 break;
3587 case KeyEvent.KEYCODE_5:
3588 case KeyEvent.KEYCODE_6:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003589 dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003590 break;
3591 case KeyEvent.KEYCODE_7:
3592 case KeyEvent.KEYCODE_8:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003593 dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003594 break;
3595 case KeyEvent.KEYCODE_9:
3596 nativeInstrumentReport();
3597 return true;
3598 }
3599 }
3600
Derek Sollenberger718d69f2009-10-19 15:56:43 -04003601 if (nativeCursorIsTextInput()) {
Leon Scroggins1cc24202009-06-16 10:10:28 -04003602 // This message will put the node in focus, for the DOM's notion
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003603 // of focus, and make the focuscontroller active
Leon Scroggins01058282009-07-30 16:33:56 -04003604 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
3605 nativeCursorNodePointer());
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003606 // This will bring up the WebTextView and put it in focus, for
3607 // our view system's notion of focus
3608 rebuildWebTextView();
3609 // Now we need to pass the event to it
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003610 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003611 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003612 return mWebTextView.dispatchKeyEvent(event);
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003613 }
Leon Scroggins40981262009-07-01 10:57:47 -04003614 } else if (nativeHasFocusNode()) {
3615 // In this case, the cursor is not on a text input, but the focus
3616 // might be. Check it, and if so, hand over to the WebTextView.
3617 rebuildWebTextView();
3618 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003619 return mWebTextView.dispatchKeyEvent(event);
Leon Scroggins40981262009-07-01 10:57:47 -04003620 }
Cary Clark19436562009-06-04 16:25:07 -04003621 }
3622
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003623 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003624 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003625 // pass the key to DOM
3626 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
3627 // return true as DOM handles the key
3628 return true;
3629 }
3630
3631 // Bubble up the key event as WebView doesn't handle it
3632 return false;
3633 }
3634
3635 @Override
3636 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003637 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003638 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003639 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003640 }
3641
3642 if (mNativeClass == 0) {
3643 return false;
3644 }
3645
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003646 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04003647 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
3648 String text = nativeCursorText();
3649 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003650 && text.startsWith(SCHEME_TEL)) {
3651 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
3652 getContext().startActivity(intent);
3653 return true;
3654 }
3655 }
3656
3657 // Bubble up the key event if
3658 // 1. it is a system key; or
3659 // 2. the host application wants to handle it;
3660 if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
3661 return false;
3662 }
3663
Cary Clarkd6982c92009-05-29 11:02:22 -04003664 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003665 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
3666 if (commitCopy()) {
3667 return true;
3668 }
3669 }
3670
3671 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3672 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3673 // always handle the navigation keys in the UI thread
3674 // Bubble up the key event as WebView doesn't handle it
3675 return false;
3676 }
3677
Leon Scrogginse3225672009-06-03 15:53:13 -04003678 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003679 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04003680 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
3681 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003682
Leon Scrogginse3225672009-06-03 15:53:13 -04003683 if (mShiftIsPressed) {
Cary Clarkc05af372009-10-16 10:52:27 -04003684 if (mExtendSelection) {
3685 commitCopy();
3686 } else {
3687 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04003688 invalidate(); // draw the i-beam instead of the arrow
Cary Clarkc05af372009-10-16 10:52:27 -04003689 }
3690 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003691 }
Grace Klobadd817492009-09-14 10:17:06 -07003692
3693 // perform the single click
3694 Rect visibleRect = sendOurVisibleRect();
3695 // Note that sendOurVisibleRect calls viewToContent, so the
3696 // coordinates should be in content coordinates.
3697 if (!nativeCursorIntersects(visibleRect)) {
3698 return false;
3699 }
Grace Klobadd817492009-09-14 10:17:06 -07003700 WebViewCore.CursorData data = cursorData();
3701 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
3702 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003703 if (nativeCursorIsTextInput()) {
3704 rebuildWebTextView();
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003705 centerKeyPressOnTextField();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003706 if (inEditingMode()) {
3707 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003708 }
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003709 return true;
3710 }
3711 nativeSetFollowedLink(true);
3712 if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
Grace Klobadd817492009-09-14 10:17:06 -07003713 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
3714 nativeCursorNodePointer());
3715 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003716 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003717 }
3718
3719 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003720 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003721 // pass the key to DOM
3722 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
3723 // return true as DOM handles the key
3724 return true;
3725 }
3726
3727 // Bubble up the key event as WebView doesn't handle it
3728 return false;
3729 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003730
Cary Clark09e383c2009-10-26 16:43:58 -04003731 private void setUpSelectXY() {
3732 mExtendSelection = false;
3733 mShiftIsPressed = true;
3734 if (nativeHasCursorNode()) {
3735 Rect rect = nativeCursorNodeBounds();
3736 mSelectX = contentToViewX(rect.left);
3737 mSelectY = contentToViewY(rect.top);
3738 } else if (mLastTouchY > getVisibleTitleHeight()) {
3739 mSelectX = mScrollX + (int) mLastTouchX;
3740 mSelectY = mScrollY + (int) mLastTouchY;
3741 } else {
3742 mSelectX = mScrollX + getViewWidth() / 2;
3743 mSelectY = mScrollY + getViewHeightWithTitle() / 2;
3744 }
3745 nativeHideCursor();
3746 }
3747
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003748 public void emulateShiftHeld() {
Cary Clark7f970112009-10-15 15:29:08 -04003749 if (0 == mNativeClass) return; // client isn't initialized
Cary Clark09e383c2009-10-26 16:43:58 -04003750 setUpSelectXY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003751 }
3752
3753 private boolean commitCopy() {
3754 boolean copiedSomething = false;
3755 if (mExtendSelection) {
Cary Clark57d2c3a2009-12-23 13:57:15 -05003756 String selection = nativeGetSelection();
3757 if (selection != "") {
3758 if (DebugFlags.WEB_VIEW) {
3759 Log.v(LOGTAG, "commitCopy \"" + selection + "\"");
3760 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003761 Toast.makeText(mContext
3762 , com.android.internal.R.string.text_copied
3763 , Toast.LENGTH_SHORT).show();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003764 copiedSomething = true;
Cary Clark57d2c3a2009-12-23 13:57:15 -05003765 try {
3766 IClipboard clip = IClipboard.Stub.asInterface(
3767 ServiceManager.getService("clipboard"));
3768 clip.setClipboardText(selection);
3769 } catch (android.os.RemoteException e) {
3770 Log.e(LOGTAG, "Clipboard failed", e);
3771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003772 }
3773 mExtendSelection = false;
3774 }
3775 mShiftIsPressed = false;
Cary Clark09e383c2009-10-26 16:43:58 -04003776 invalidate(); // remove selection region and pointer
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003777 if (mTouchMode == TOUCH_SELECT_MODE) {
3778 mTouchMode = TOUCH_INIT_MODE;
3779 }
3780 return copiedSomething;
3781 }
3782
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003783 @Override
3784 protected void onAttachedToWindow() {
3785 super.onAttachedToWindow();
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003786 if (hasWindowFocus()) onWindowFocusChanged(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003787 }
3788
3789 @Override
3790 protected void onDetachedFromWindow() {
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003791 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003792 super.onDetachedFromWindow();
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003793 // Clean up the zoom controller
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003794 mZoomButtonsController.setVisible(false);
3795 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003796
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003797 /**
3798 * @deprecated WebView no longer needs to implement
3799 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
3800 */
3801 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003802 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04003803
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003804 /**
3805 * @deprecated WebView no longer needs to implement
3806 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
3807 */
3808 @Deprecated
3809 public void onChildViewRemoved(View p, View child) {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003810
3811 /**
3812 * @deprecated WebView should not have implemented
3813 * ViewTreeObserver.OnGlobalFocusChangeListener. This method
3814 * does nothing now.
3815 */
3816 @Deprecated
3817 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
3818 }
3819
Cary Clarkd6982c92009-05-29 11:02:22 -04003820 // To avoid drawing the cursor ring, and remove the TextView when our window
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003821 // loses focus.
3822 @Override
3823 public void onWindowFocusChanged(boolean hasWindowFocus) {
3824 if (hasWindowFocus) {
3825 if (hasFocus()) {
3826 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003827 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04003828 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003829 if (mNativeClass != 0) {
3830 nativeRecordButtons(true, false, true);
Leon Scroggins8cdad882009-06-30 08:47:04 -04003831 if (inEditingMode()) {
3832 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
3833 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003834 }
3835 } else {
3836 // If our window gained focus, but we do not have it, do not
Cary Clarkd6982c92009-05-29 11:02:22 -04003837 // draw the cursor ring.
3838 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003839 // We do not call nativeRecordButtons here because we assume
3840 // that when we lost focus, or window focus, it got called with
3841 // false for the first parameter
3842 }
3843 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07003844 if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003845 /*
3846 * The zoom controls come in their own window, so our window
Cary Clarkd6982c92009-05-29 11:02:22 -04003847 * loses focus. Our policy is to not draw the cursor ring if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003848 * our window is not focused, but this is an exception since
3849 * the user can still navigate the web page with the zoom
3850 * controls showing.
3851 */
Cary Clarkd6982c92009-05-29 11:02:22 -04003852 // If our window has lost focus, stop drawing the cursor ring
3853 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003854 }
3855 mGotKeyDown = false;
3856 mShiftIsPressed = false;
3857 if (mNativeClass != 0) {
3858 nativeRecordButtons(false, false, true);
3859 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003860 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003861 }
3862 invalidate();
3863 super.onWindowFocusChanged(hasWindowFocus);
3864 }
3865
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003866 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003867 * Pass a message to WebCore Thread, telling the WebCore::Page's
3868 * FocusController to be "inactive" so that it will
3869 * not draw the blinking cursor. It gets set to "active" to draw the cursor
3870 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003871 */
Leon Scroggins01058282009-07-30 16:33:56 -04003872 /* package */ void setFocusControllerInactive() {
Leon Scrogginsfd06bc82009-06-08 13:22:02 -04003873 // Do not need to also check whether mWebViewCore is null, because
3874 // mNativeClass is only set if mWebViewCore is non null
3875 if (mNativeClass == 0) return;
Leon Scroggins8cdad882009-06-30 08:47:04 -04003876 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003877 }
3878
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003879 @Override
3880 protected void onFocusChanged(boolean focused, int direction,
3881 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003882 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003883 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
3884 }
3885 if (focused) {
3886 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003887 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003888 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003889 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003890 if (mNativeClass != 0) {
3891 nativeRecordButtons(true, false, true);
3892 }
3893 //} else {
3894 // The WebView has gained focus while we do not have
3895 // windowfocus. When our window lost focus, we should have
3896 // called nativeRecordButtons(false...)
3897 }
3898 } else {
3899 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04003900 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003901 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003902 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003903 if (mNativeClass != 0) {
3904 nativeRecordButtons(false, false, true);
3905 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003906 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003907 }
3908 mGotKeyDown = false;
3909 }
3910
3911 super.onFocusChanged(focused, direction, previouslyFocusedRect);
3912 }
3913
Grace Kloba3f9faf42009-10-13 14:13:54 -07003914 /**
3915 * @hide
3916 */
3917 @Override
3918 protected boolean setFrame(int left, int top, int right, int bottom) {
3919 boolean changed = super.setFrame(left, top, right, bottom);
3920 if (!changed && mHeightCanMeasure) {
3921 // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
3922 // in WebViewCore after we get the first layout. We do call
3923 // requestLayout() when we get contentSizeChanged(). But the View
3924 // system won't call onSizeChanged if the dimension is not changed.
3925 // In this case, we need to call sendViewSizeZoom() explicitly to
3926 // notify the WebKit about the new dimensions.
3927 sendViewSizeZoom();
3928 }
3929 return changed;
3930 }
3931
Grace Kloba3a0def22010-01-23 21:11:54 -08003932 private static class PostScale implements Runnable {
3933 final WebView mWebView;
3934 final boolean mUpdateTextWrap;
3935
3936 public PostScale(WebView webView, boolean updateTextWrap) {
3937 mWebView = webView;
3938 mUpdateTextWrap = updateTextWrap;
3939 }
3940
3941 public void run() {
3942 if (mWebView.mWebViewCore != null) {
3943 // we always force, in case our height changed, in which case we
3944 // still want to send the notification over to webkit.
3945 mWebView.setNewZoomScale(mWebView.mActualScale,
3946 mUpdateTextWrap, true);
3947 }
3948 }
3949 }
3950
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003951 @Override
3952 protected void onSizeChanged(int w, int h, int ow, int oh) {
3953 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003954 // Center zooming to the center of the screen.
Cary Clarka91874d2009-08-24 14:08:43 -04003955 if (mZoomScale == 0) { // unless we're already zooming
3956 mZoomCenterX = getViewWidth() * .5f;
3957 mZoomCenterY = getViewHeight() * .5f;
Grace Kloba3a0def22010-01-23 21:11:54 -08003958 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
3959 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Cary Clarka91874d2009-08-24 14:08:43 -04003960 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003961
Grace Klobaa4fa1072009-11-09 12:01:50 -08003962 // adjust the max viewport width depending on the view dimensions. This
3963 // is to ensure the scaling is not going insane. So do not shrink it if
3964 // the view size is temporarily smaller, e.g. when soft keyboard is up.
3965 int newMaxViewportWidth = (int) (Math.max(w, h) / DEFAULT_MIN_ZOOM_SCALE);
3966 if (newMaxViewportWidth > sMaxViewportWidth) {
3967 sMaxViewportWidth = newMaxViewportWidth;
3968 }
3969
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003970 // update mMinZoomScale if the minimum zoom scale is not fixed
3971 if (!mMinZoomScaleFixed) {
Grace Klobaba672802009-09-25 15:54:08 -07003972 // when change from narrow screen to wide screen, the new viewWidth
3973 // can be wider than the old content width. We limit the minimum
3974 // scale to 1.0f. The proper minimum scale will be calculated when
3975 // the new picture shows up.
3976 mMinZoomScale = Math.min(1.0f, (float) getViewWidth()
Grace Klobae397a882009-08-06 12:04:14 -07003977 / (mDrawHistory ? mHistoryPicture.getWidth()
Grace Klobaba672802009-09-25 15:54:08 -07003978 : mZoomOverviewWidth));
Grace Kloba16efce72009-11-10 15:49:03 -08003979 if (mInitialScaleInPercent > 0) {
3980 // limit the minZoomScale to the initialScale if it is set
3981 float initialScale = mInitialScaleInPercent / 100.0f;
3982 if (mMinZoomScale > initialScale) {
3983 mMinZoomScale = initialScale;
3984 }
3985 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003986 }
3987
Grace Kloba70b942d2009-12-11 19:33:04 -08003988 // onSizeChanged() is called during WebView layout. And any
3989 // requestLayout() is blocked during layout. As setNewZoomScale() will
3990 // call its child View to reposition itself through ViewManager's
3991 // scaleAll(), we need to post a Runnable to ensure requestLayout().
Grace Kloba3a0def22010-01-23 21:11:54 -08003992 // <b/>
3993 // only update the text wrap scale if width changed.
3994 post(new PostScale(this, w != ow));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003995 }
3996
3997 @Override
3998 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
3999 super.onScrollChanged(l, t, oldl, oldt);
Patrick Scott0a5ce012009-07-02 08:56:10 -04004000
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004001 sendOurVisibleRect();
4002 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004003
4004
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004005 @Override
4006 public boolean dispatchKeyEvent(KeyEvent event) {
4007 boolean dispatch = true;
4008
4009 if (!inEditingMode()) {
4010 if (event.getAction() == KeyEvent.ACTION_DOWN) {
4011 mGotKeyDown = true;
4012 } else {
4013 if (!mGotKeyDown) {
4014 /*
4015 * We got a key up for which we were not the recipient of
4016 * the original key down. Don't give it to the view.
4017 */
4018 dispatch = false;
4019 }
4020 mGotKeyDown = false;
4021 }
4022 }
4023
4024 if (dispatch) {
4025 return super.dispatchKeyEvent(event);
4026 } else {
4027 // We didn't dispatch, so let something else handle the key
4028 return false;
4029 }
4030 }
4031
4032 // Here are the snap align logic:
4033 // 1. If it starts nearly horizontally or vertically, snap align;
4034 // 2. If there is a dramitic direction change, let it go;
4035 // 3. If there is a same direction back and forth, lock it.
4036
4037 // adjustable parameters
4038 private int mMinLockSnapReverseDistance;
4039 private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
4040 private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
4041
Mike Reed19f3f0e2009-11-12 12:50:20 -05004042 private static int sign(float x) {
4043 return x > 0 ? 1 : (x < 0 ? -1 : 0);
4044 }
4045
4046 // if the page can scroll <= this value, we won't allow the drag tracker
4047 // to have any effect.
4048 private static final int MIN_SCROLL_AMOUNT_TO_DISABLE_DRAG_TRACKER = 4;
4049
4050 private class DragTrackerHandler {
4051 private final DragTracker mProxy;
4052 private final float mStartY, mStartX;
4053 private final float mMinDY, mMinDX;
4054 private final float mMaxDY, mMaxDX;
4055 private float mCurrStretchY, mCurrStretchX;
4056 private int mSX, mSY;
4057
4058 public DragTrackerHandler(float x, float y, DragTracker proxy) {
4059 mProxy = proxy;
4060
4061 int docBottom = computeVerticalScrollRange() + getTitleHeight();
4062 int viewTop = getScrollY();
4063 int viewBottom = viewTop + getHeight();
4064
4065 mStartY = y;
4066 mMinDY = -viewTop;
4067 mMaxDY = docBottom - viewBottom;
4068
Mike Reedfdc54242010-01-08 10:29:51 -05004069 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004070 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " dragtracker y= " + y +
4071 " up/down= " + mMinDY + " " + mMaxDY);
4072 }
4073
4074 int docRight = computeHorizontalScrollRange();
4075 int viewLeft = getScrollX();
4076 int viewRight = viewLeft + getWidth();
4077 mStartX = x;
4078 mMinDX = -viewLeft;
4079 mMaxDX = docRight - viewRight;
4080
4081 mProxy.onStartDrag(x, y);
4082
4083 // ensure we buildBitmap at least once
4084 mSX = -99999;
4085 }
4086
4087 private float computeStretch(float delta, float min, float max) {
4088 float stretch = 0;
4089 if (max - min > MIN_SCROLL_AMOUNT_TO_DISABLE_DRAG_TRACKER) {
4090 if (delta < min) {
4091 stretch = delta - min;
4092 } else if (delta > max) {
4093 stretch = delta - max;
4094 }
4095 }
4096 return stretch;
4097 }
4098
4099 public void dragTo(float x, float y) {
4100 float sy = computeStretch(mStartY - y, mMinDY, mMaxDY);
4101 float sx = computeStretch(mStartX - x, mMinDX, mMaxDX);
4102
4103 if (mCurrStretchX != sx || mCurrStretchY != sy) {
4104 mCurrStretchX = sx;
4105 mCurrStretchY = sy;
Mike Reedfdc54242010-01-08 10:29:51 -05004106 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004107 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "---- stretch " + sx +
4108 " " + sy);
4109 }
4110 if (mProxy.onStretchChange(sx, sy)) {
4111 invalidate();
4112 }
4113 }
4114 }
4115
4116 public void stopDrag() {
Mike Reedfdc54242010-01-08 10:29:51 -05004117 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004118 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag");
4119 }
4120 mProxy.onStopDrag();
4121 }
4122
4123 private int hiddenHeightOfTitleBar() {
4124 return getTitleHeight() - getVisibleTitleHeight();
4125 }
4126
4127 // need a way to know if 565 or 8888 is the right config for
4128 // capturing the display and giving it to the drag proxy
4129 private Bitmap.Config offscreenBitmapConfig() {
4130 // hard code 565 for now
4131 return Bitmap.Config.RGB_565;
4132 }
4133
4134 /* If the tracker draws, then this returns true, otherwise it will
4135 return false, and draw nothing.
4136 */
4137 public boolean draw(Canvas canvas) {
4138 if (mCurrStretchX != 0 || mCurrStretchY != 0) {
4139 int sx = getScrollX();
4140 int sy = getScrollY() - hiddenHeightOfTitleBar();
4141
4142 if (mSX != sx || mSY != sy) {
4143 buildBitmap(sx, sy);
4144 mSX = sx;
4145 mSY = sy;
4146 }
4147
4148 int count = canvas.save(Canvas.MATRIX_SAVE_FLAG);
4149 canvas.translate(sx, sy);
4150 mProxy.onDraw(canvas);
4151 canvas.restoreToCount(count);
4152 return true;
4153 }
Mike Reedfdc54242010-01-08 10:29:51 -05004154 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004155 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " -- draw false " +
4156 mCurrStretchX + " " + mCurrStretchY);
4157 }
4158 return false;
4159 }
4160
4161 private void buildBitmap(int sx, int sy) {
4162 int w = getWidth();
4163 int h = getViewHeight();
4164 Bitmap bm = Bitmap.createBitmap(w, h, offscreenBitmapConfig());
4165 Canvas canvas = new Canvas(bm);
4166 canvas.translate(-sx, -sy);
4167 drawContent(canvas);
4168
Mike Reedfdc54242010-01-08 10:29:51 -05004169 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004170 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "--- buildBitmap " + sx +
4171 " " + sy + " " + w + " " + h);
4172 }
4173 mProxy.onBitmapChange(bm);
4174 }
4175 }
4176
4177 /** @hide */
4178 public static class DragTracker {
4179 public void onStartDrag(float x, float y) {}
4180 public boolean onStretchChange(float sx, float sy) {
4181 // return true to have us inval the view
4182 return false;
4183 }
4184 public void onStopDrag() {}
4185 public void onBitmapChange(Bitmap bm) {}
4186 public void onDraw(Canvas canvas) {}
4187 }
4188
4189 /** @hide */
4190 public DragTracker getDragTracker() {
4191 return mDragTracker;
4192 }
4193
4194 /** @hide */
4195 public void setDragTracker(DragTracker tracker) {
4196 mDragTracker = tracker;
4197 }
4198
4199 private DragTracker mDragTracker;
4200 private DragTrackerHandler mDragTrackerHandler;
4201
Grace Kloba3a0def22010-01-23 21:11:54 -08004202 private class ScaleDetectorListener implements
4203 ScaleGestureDetector.OnScaleGestureListener {
4204
4205 public boolean onScaleBegin(ScaleGestureDetector detector) {
4206 // cancel the single touch handling
4207 cancelTouch();
4208 if (mZoomButtonsController.isVisible()) {
4209 mZoomButtonsController.setVisible(false);
4210 }
4211 // reset the zoom overview mode so that the page won't auto grow
4212 mInZoomOverview = false;
4213 // If it is in password mode, turn it off so it does not draw
4214 // misplaced.
4215 if (inEditingMode() && nativeFocusCandidateIsPassword()) {
4216 mWebTextView.setInPassword(false);
4217 }
4218 return true;
4219 }
4220
4221 public void onScaleEnd(ScaleGestureDetector detector) {
4222 if (mPreviewZoomOnly) {
4223 mPreviewZoomOnly = false;
4224 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
4225 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
4226 // don't reflow when zoom in; when zoom out, do reflow if the
4227 // new scale is almost minimum scale;
4228 boolean reflowNow = (mActualScale - mMinZoomScale <= 0.01f)
4229 || ((mActualScale <= 0.8 * mTextWrapScale));
4230 // force zoom after mPreviewZoomOnly is set to false so that the
4231 // new view size will be passed to the WebKit
4232 setNewZoomScale(mActualScale, reflowNow, true);
4233 // call invalidate() to draw without zoom filter
4234 invalidate();
4235 }
4236 // adjust the edit text view if needed
4237 if (inEditingMode() && didUpdateTextViewBounds(false)
4238 && nativeFocusCandidateIsPassword()) {
4239 // If it is a password field, start drawing the
4240 // WebTextView once again.
4241 mWebTextView.setInPassword(true);
4242 }
4243 // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as it
4244 // may trigger the unwanted click, can't use TOUCH_DRAG_MODE as it
4245 // may trigger the unwanted fling.
4246 mTouchMode = TOUCH_PINCH_DRAG;
4247 startTouch(detector.getFocusX(), detector.getFocusY(),
4248 mLastTouchTime);
4249 }
4250
4251 public boolean onScale(ScaleGestureDetector detector) {
4252 float scale = (float) (Math.round(detector.getScaleFactor()
4253 * mActualScale * 100) / 100.0);
4254 if (Math.abs(scale - mActualScale) >= PREVIEW_SCALE_INCREMENT) {
4255 mPreviewZoomOnly = true;
4256 // limit the scale change per step
4257 if (scale > mActualScale) {
4258 scale = Math.min(scale, mActualScale * 1.25f);
4259 } else {
4260 scale = Math.max(scale, mActualScale * 0.8f);
4261 }
4262 mZoomCenterX = detector.getFocusX();
4263 mZoomCenterY = detector.getFocusY();
4264 setNewZoomScale(scale, false, false);
4265 invalidate();
4266 return true;
4267 }
4268 return false;
4269 }
4270 }
4271
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004272 @Override
4273 public boolean onTouchEvent(MotionEvent ev) {
4274 if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
4275 return false;
4276 }
4277
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004278 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004279 Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
4280 + mTouchMode);
4281 }
4282
Grace Kloba3a0def22010-01-23 21:11:54 -08004283 int action;
4284 float x, y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004285 long eventTime = ev.getEventTime();
4286
Grace Kloba3a0def22010-01-23 21:11:54 -08004287 // FIXME: we may consider to give WebKit an option to handle multi-touch
4288 // events later.
4289 if (mSupportMultiTouch && ev.getPointerCount() > 1) {
4290 if (mMinZoomScale < mMaxZoomScale) {
4291 mScaleDetector.onTouchEvent(ev);
4292 if (mScaleDetector.isInProgress()) {
4293 mLastTouchTime = eventTime;
4294 return true;
4295 }
4296 x = mScaleDetector.getFocusX();
4297 y = mScaleDetector.getFocusY();
4298 action = ev.getAction() & MotionEvent.ACTION_MASK;
4299 if (action == MotionEvent.ACTION_POINTER_DOWN) {
4300 cancelTouch();
4301 action = MotionEvent.ACTION_DOWN;
4302 } else if (action == MotionEvent.ACTION_POINTER_UP) {
4303 // set mLastTouchX/Y to the remaining point
4304 mLastTouchX = x;
4305 mLastTouchY = y;
4306 } else if (action == MotionEvent.ACTION_MOVE) {
4307 // negative x or y indicate it is on the edge, skip it.
4308 if (x < 0 || y < 0) {
4309 return true;
4310 }
4311 }
4312 } else {
4313 // if the page disallow zoom, skip multi-pointer action
4314 return true;
4315 }
4316 } else {
4317 action = ev.getAction();
4318 x = ev.getX();
4319 y = ev.getY();
4320 }
4321
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004322 // Due to the touch screen edge effect, a touch closer to the edge
4323 // always snapped to the edge. As getViewWidth() can be different from
4324 // getWidth() due to the scrollbar, adjusting the point to match
4325 // getViewWidth(). Same applied to the height.
4326 if (x > getViewWidth() - 1) {
4327 x = getViewWidth() - 1;
4328 }
Grace Kloba8eff73f2009-09-24 09:34:32 -07004329 if (y > getViewHeightWithTitle() - 1) {
4330 y = getViewHeightWithTitle() - 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004331 }
4332
4333 // pass the touch events from UI thread to WebCore thread
Grace Kloba04b28682009-09-14 14:38:37 -07004334 if (mForwardTouchEvents && (action != MotionEvent.ACTION_MOVE
4335 || eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004336 WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
4337 ted.mAction = action;
Leon Scroggins0236e672009-09-02 21:12:08 -04004338 ted.mX = viewToContentX((int) x + mScrollX);
4339 ted.mY = viewToContentY((int) y + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004340 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
4341 mLastSentTouchTime = eventTime;
4342 }
4343
Cary Clark25415e22009-10-12 13:41:28 -04004344 float fDeltaX = mLastTouchX - x;
4345 float fDeltaY = mLastTouchY - y;
4346 int deltaX = (int) fDeltaX;
4347 int deltaY = (int) fDeltaY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004348
4349 switch (action) {
4350 case MotionEvent.ACTION_DOWN: {
Grace Klobad66d84f2009-09-27 14:48:07 -07004351 mPreventDrag = PREVENT_DRAG_NO;
Grace Kloba04b28682009-09-14 14:38:37 -07004352 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04004353 // stop the current scroll animation, but if this is
4354 // the start of a fling, allow it to add to the current
4355 // fling's velocity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004356 mScroller.abortAnimation();
4357 mTouchMode = TOUCH_DRAG_START_MODE;
Grace Kloba96949ef2010-01-25 09:53:01 -08004358 mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004359 } else if (mShiftIsPressed) {
4360 mSelectX = mScrollX + (int) x;
4361 mSelectY = mScrollY + (int) y;
4362 mTouchMode = TOUCH_SELECT_MODE;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004363 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004364 Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
4365 }
Leon Scroggins0236e672009-09-02 21:12:08 -04004366 nativeMoveSelection(viewToContentX(mSelectX),
4367 viewToContentY(mSelectY), false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004368 mTouchSelection = mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04004369 invalidate(); // draw the i-beam instead of the arrow
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004370 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
4371 mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
4372 if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
4373 mTouchMode = TOUCH_DOUBLE_TAP_MODE;
4374 } else {
4375 // commit the short press action for the previous tap
4376 doShortPress();
4377 // continue, mTouchMode should be still TOUCH_INIT_MODE
4378 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004379 } else {
Grace Kloba3a0def22010-01-23 21:11:54 -08004380 mPreviewZoomOnly = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004381 mTouchMode = TOUCH_INIT_MODE;
Grace Klobaf58af622009-09-24 17:41:23 -07004382 mPreventDrag = mForwardTouchEvents ? PREVENT_DRAG_MAYBE_YES
4383 : PREVENT_DRAG_NO;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004384 mPreventLongPress = false;
4385 mPreventDoubleTap = false;
Cary Clark77d98f42009-07-31 09:40:38 -04004386 mWebViewCore.sendMessage(
4387 EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004388 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
4389 EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
4390 (eventTime - mLastTouchUpTime), eventTime);
4391 }
4392 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004393 // Trigger the link
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004394 if (mTouchMode == TOUCH_INIT_MODE
4395 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004396 mPrivateHandler.sendMessageDelayed(mPrivateHandler
4397 .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
4398 }
Grace Kloba3a0def22010-01-23 21:11:54 -08004399 startTouch(x, y, eventTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004400 break;
4401 }
4402 case MotionEvent.ACTION_MOVE: {
Grace Kloba04b28682009-09-14 14:38:37 -07004403 if (mTouchMode == TOUCH_DONE_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004404 // no dragging during scroll zoom animation
4405 break;
4406 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004407 mVelocityTracker.addMovement(ev);
4408
4409 if (mTouchMode != TOUCH_DRAG_MODE) {
4410 if (mTouchMode == TOUCH_SELECT_MODE) {
4411 mSelectX = mScrollX + (int) x;
4412 mSelectY = mScrollY + (int) y;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004413 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004414 Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
4415 }
Leon Scroggins0236e672009-09-02 21:12:08 -04004416 nativeMoveSelection(viewToContentX(mSelectX),
4417 viewToContentY(mSelectY), true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004418 invalidate();
4419 break;
4420 }
Grace Klobaf58af622009-09-24 17:41:23 -07004421 if ((deltaX * deltaX + deltaY * deltaY) < mTouchSlopSquare) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004422 break;
4423 }
Grace Klobaf58af622009-09-24 17:41:23 -07004424 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
4425 // track mLastTouchTime as we may need to do fling at
4426 // ACTION_UP
4427 mLastTouchTime = eventTime;
4428 break;
4429 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004430 if (mTouchMode == TOUCH_SHORTPRESS_MODE
4431 || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
4432 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004433 } else if (mTouchMode == TOUCH_INIT_MODE
4434 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004435 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4436 }
Grace Kloba11438c32009-12-16 11:39:12 -08004437 if (mFullScreenHolder != null) {
4438 // in full screen mode, the WebView can't be panned.
4439 mTouchMode = TOUCH_DONE_MODE;
4440 break;
4441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004442
4443 // if it starts nearly horizontal or vertical, enforce it
4444 int ax = Math.abs(deltaX);
4445 int ay = Math.abs(deltaY);
4446 if (ax > MAX_SLOPE_FOR_DIAG * ay) {
4447 mSnapScrollMode = SNAP_X;
4448 mSnapPositive = deltaX > 0;
4449 } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
4450 mSnapScrollMode = SNAP_Y;
4451 mSnapPositive = deltaY > 0;
4452 }
4453
4454 mTouchMode = TOUCH_DRAG_MODE;
Romain Guyf7b4acc2009-12-01 16:24:45 -08004455 mLastTouchX = x;
4456 mLastTouchY = y;
4457 fDeltaX = 0.0f;
4458 fDeltaY = 0.0f;
4459 deltaX = 0;
4460 deltaY = 0;
4461
Grace Klobaa7bc87c2010-01-29 14:56:25 -08004462 WebViewCore.reducePriority();
Leon Scroggins72543e12009-07-23 15:29:45 -04004463 if (!mDragFromTextInput) {
4464 nativeHideCursor();
4465 }
The Android Open Source Project10592532009-03-18 17:39:46 -07004466 WebSettings settings = getSettings();
Grace Kloba455e3af2009-08-13 11:01:21 -07004467 if (settings.supportZoom()
The Android Open Source Project10592532009-03-18 17:39:46 -07004468 && settings.getBuiltInZoomControls()
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004469 && !mZoomButtonsController.isVisible()
Grace Kloba04b28682009-09-14 14:38:37 -07004470 && mMinZoomScale < mMaxZoomScale) {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004471 mZoomButtonsController.setVisible(true);
Grace Klobaf8d8b462009-09-20 15:57:49 -07004472 int count = settings.getDoubleTapToastCount();
4473 if (mInZoomOverview && count > 0) {
Grace Kloba24a3ff92009-09-22 10:42:22 -07004474 settings.setDoubleTapToastCount(--count);
Grace Klobaf8d8b462009-09-20 15:57:49 -07004475 Toast.makeText(mContext,
4476 com.android.internal.R.string.double_tap_toast,
Grace Kloba24a3ff92009-09-22 10:42:22 -07004477 Toast.LENGTH_LONG).show();
Grace Klobaf8d8b462009-09-20 15:57:49 -07004478 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004479 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004480 }
4481
4482 // do pan
4483 int newScrollX = pinLocX(mScrollX + deltaX);
Cary Clark25415e22009-10-12 13:41:28 -04004484 int newDeltaX = newScrollX - mScrollX;
4485 if (deltaX != newDeltaX) {
4486 deltaX = newDeltaX;
4487 fDeltaX = (float) newDeltaX;
4488 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004489 int newScrollY = pinLocY(mScrollY + deltaY);
Cary Clark25415e22009-10-12 13:41:28 -04004490 int newDeltaY = newScrollY - mScrollY;
4491 if (deltaY != newDeltaY) {
4492 deltaY = newDeltaY;
4493 fDeltaY = (float) newDeltaY;
4494 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004495 boolean done = false;
Cary Clark25415e22009-10-12 13:41:28 -04004496 boolean keepScrollBarsVisible = false;
4497 if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
4498 keepScrollBarsVisible = done = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004499 } else {
4500 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
4501 int ax = Math.abs(deltaX);
4502 int ay = Math.abs(deltaY);
4503 if (mSnapScrollMode == SNAP_X) {
4504 // radical change means getting out of snap mode
4505 if (ay > MAX_SLOPE_FOR_DIAG * ax
4506 && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
4507 mSnapScrollMode = SNAP_NONE;
4508 }
4509 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004510 if (ax > MAX_SLOPE_FOR_DIAG * ay &&
4511 (mSnapPositive
4512 ? deltaX < -mMinLockSnapReverseDistance
4513 : deltaX > mMinLockSnapReverseDistance)) {
4514 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004515 }
4516 } else {
4517 // radical change means getting out of snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004518 if (ax > MAX_SLOPE_FOR_DIAG * ay
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004519 && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
4520 mSnapScrollMode = SNAP_NONE;
4521 }
4522 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004523 if (ay > MAX_SLOPE_FOR_DIAG * ax &&
4524 (mSnapPositive
4525 ? deltaY < -mMinLockSnapReverseDistance
4526 : deltaY > mMinLockSnapReverseDistance)) {
4527 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004528 }
4529 }
4530 }
Cary Clarkac492e12009-10-14 14:53:37 -04004531 if (mSnapScrollMode != SNAP_NONE) {
4532 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
4533 deltaY = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07004534 } else {
Cary Clarkac492e12009-10-14 14:53:37 -04004535 deltaX = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07004536 }
Cary Clarkac492e12009-10-14 14:53:37 -04004537 }
4538 if ((deltaX | deltaY) != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004539 scrollBy(deltaX, deltaY);
Cary Clarkac492e12009-10-14 14:53:37 -04004540 if (deltaX != 0) {
4541 mLastTouchX = x;
4542 }
4543 if (deltaY != 0) {
4544 mLastTouchY = y;
4545 }
4546 mHeldMotionless = MOTIONLESS_FALSE;
4547 } else {
4548 // keep the scrollbar on the screen even there is no
4549 // scroll
4550 keepScrollBarsVisible = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004551 }
4552 mLastTouchTime = eventTime;
4553 mUserScroll = true;
4554 }
The Android Open Source Project10592532009-03-18 17:39:46 -07004555
Grace Kloba455e3af2009-08-13 11:01:21 -07004556 if (!getSettings().getBuiltInZoomControls()) {
The Android Open Source Project10592532009-03-18 17:39:46 -07004557 boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
Grace Kloba04b28682009-09-14 14:38:37 -07004558 if (mZoomControls != null && showPlusMinus) {
The Android Open Source Project10592532009-03-18 17:39:46 -07004559 if (mZoomControls.getVisibility() == View.VISIBLE) {
4560 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4561 } else {
Grace Kloba04b28682009-09-14 14:38:37 -07004562 mZoomControls.show(showPlusMinus, false);
The Android Open Source Project10592532009-03-18 17:39:46 -07004563 }
4564 mPrivateHandler.postDelayed(mZoomControlRunnable,
4565 ZOOM_CONTROLS_TIMEOUT);
4566 }
4567 }
4568
Mike Reed19f3f0e2009-11-12 12:50:20 -05004569 if (mDragTrackerHandler != null) {
4570 mDragTrackerHandler.dragTo(x, y);
4571 }
4572
Cary Clark25415e22009-10-12 13:41:28 -04004573 if (keepScrollBarsVisible) {
4574 if (mHeldMotionless != MOTIONLESS_TRUE) {
4575 mHeldMotionless = MOTIONLESS_TRUE;
4576 invalidate();
4577 }
Grace Kloba5b2c0562009-09-30 10:17:13 -07004578 // keep the scrollbar on the screen even there is no scroll
4579 awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
4580 false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004581 // return false to indicate that we can't pan out of the
4582 // view space
Cary Clark25415e22009-10-12 13:41:28 -04004583 return !done;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004584 }
4585 break;
4586 }
4587 case MotionEvent.ACTION_UP: {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004588 if (mDragTrackerHandler != null) {
4589 mDragTrackerHandler.stopDrag();
4590 mDragTrackerHandler = null;
4591 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004592 mLastTouchUpTime = eventTime;
4593 switch (mTouchMode) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004594 case TOUCH_DOUBLE_TAP_MODE: // double tap
4595 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad3c6d542009-07-31 14:24:19 -07004596 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004597 if (mPreventDoubleTap) {
4598 WebViewCore.TouchEventData ted
4599 = new WebViewCore.TouchEventData();
4600 ted.mAction = WebViewCore.ACTION_DOUBLETAP;
4601 ted.mX = viewToContentX((int) x + mScrollX);
4602 ted.mY = viewToContentY((int) y + mScrollY);
4603 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Grace Kloba11438c32009-12-16 11:39:12 -08004604 } else if (mFullScreenHolder == null) {
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004605 doDoubleTap();
4606 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004607 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004608 case TOUCH_SELECT_MODE:
4609 commitCopy();
4610 mTouchSelection = false;
4611 break;
Grace Klobaf58af622009-09-24 17:41:23 -07004612 case TOUCH_INIT_MODE: // tap
Grace Klobad66d84f2009-09-27 14:48:07 -07004613 case TOUCH_SHORTPRESS_START_MODE:
4614 case TOUCH_SHORTPRESS_MODE:
Grace Klobaf58af622009-09-24 17:41:23 -07004615 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad66d84f2009-09-27 14:48:07 -07004616 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Klobaf58af622009-09-24 17:41:23 -07004617 if ((deltaX * deltaX + deltaY * deltaY) > mTouchSlopSquare) {
4618 Log.w(LOGTAG, "Miss a drag as we are waiting for" +
4619 " WebCore's response for touch down.");
Grace Kloba11438c32009-12-16 11:39:12 -08004620 if (mFullScreenHolder == null
4621 && (computeHorizontalScrollExtent() < computeHorizontalScrollRange()
4622 || computeVerticalScrollExtent() < computeVerticalScrollRange())) {
Grace Klobaf58af622009-09-24 17:41:23 -07004623 // we will not rewrite drag code here, but we
4624 // will try fling if it applies.
Grace Klobaa7bc87c2010-01-29 14:56:25 -08004625 WebViewCore.reducePriority();
Grace Klobaf58af622009-09-24 17:41:23 -07004626 // fall through to TOUCH_DRAG_MODE
4627 } else {
4628 break;
4629 }
4630 } else {
Grace Klobacaf0ce32010-01-25 18:36:48 -08004631 // mPreventDrag can be PREVENT_DRAG_MAYBE_YES in
4632 // TOUCH_INIT_MODE. To give WebCoreThread a little
4633 // more time to send PREVENT_TOUCH_ID, we check
4634 // again in responding RELEASE_SINGLE_TAP.
4635 if (mPreventDrag != PREVENT_DRAG_YES) {
Grace Klobad66d84f2009-09-27 14:48:07 -07004636 if (mTouchMode == TOUCH_INIT_MODE) {
4637 mPrivateHandler.sendMessageDelayed(
4638 mPrivateHandler.obtainMessage(
4639 RELEASE_SINGLE_TAP),
4640 ViewConfiguration.getDoubleTapTimeout());
4641 } else {
4642 mTouchMode = TOUCH_DONE_MODE;
4643 doShortPress();
4644 }
Grace Klobaf58af622009-09-24 17:41:23 -07004645 }
4646 break;
4647 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004648 case TOUCH_DRAG_MODE:
Cary Clark25415e22009-10-12 13:41:28 -04004649 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4650 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
4651 mHeldMotionless = MOTIONLESS_TRUE;
Mike Reeddf4cf292009-09-15 14:31:54 -04004652 // redraw in high-quality, as we're done dragging
4653 invalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004654 // if the user waits a while w/o moving before the
4655 // up, we don't want to do a fling
4656 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
4657 mVelocityTracker.addMovement(ev);
4658 doFling();
4659 break;
4660 }
Cary Clark278ce052009-08-31 16:08:42 -04004661 mLastVelocity = 0;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08004662 WebViewCore.resumePriority();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004663 break;
4664 case TOUCH_DRAG_START_MODE:
4665 case TOUCH_DONE_MODE:
4666 // do nothing
4667 break;
4668 }
4669 // we also use mVelocityTracker == null to tell us that we are
4670 // not "moving around", so we can take the slower/prettier
4671 // mode in the drawing code
4672 if (mVelocityTracker != null) {
4673 mVelocityTracker.recycle();
4674 mVelocityTracker = null;
4675 }
4676 break;
4677 }
4678 case MotionEvent.ACTION_CANCEL: {
Grace Kloba3a0def22010-01-23 21:11:54 -08004679 cancelTouch();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004680 break;
4681 }
4682 }
4683 return true;
4684 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004685
Grace Kloba3a0def22010-01-23 21:11:54 -08004686 private void startTouch(float x, float y, long eventTime) {
4687 // Remember where the motion event started
4688 mLastTouchX = x;
4689 mLastTouchY = y;
4690 mLastTouchTime = eventTime;
4691 mVelocityTracker = VelocityTracker.obtain();
4692 mSnapScrollMode = SNAP_NONE;
4693 if (mDragTracker != null) {
4694 mDragTrackerHandler = new DragTrackerHandler(x, y, mDragTracker);
4695 }
4696 }
4697
4698 private void cancelTouch() {
4699 if (mDragTrackerHandler != null) {
4700 mDragTrackerHandler.stopDrag();
4701 mDragTrackerHandler = null;
4702 }
4703 // we also use mVelocityTracker == null to tell us that we are
4704 // not "moving around", so we can take the slower/prettier
4705 // mode in the drawing code
4706 if (mVelocityTracker != null) {
4707 mVelocityTracker.recycle();
4708 mVelocityTracker = null;
4709 }
4710 if (mTouchMode == TOUCH_DRAG_MODE) {
Grace Klobaa7bc87c2010-01-29 14:56:25 -08004711 WebViewCore.resumePriority();
Grace Kloba3a0def22010-01-23 21:11:54 -08004712 }
4713 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4714 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
4715 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4716 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
4717 mHeldMotionless = MOTIONLESS_TRUE;
4718 mTouchMode = TOUCH_DONE_MODE;
4719 nativeHideCursor();
4720 }
4721
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004722 private long mTrackballFirstTime = 0;
4723 private long mTrackballLastTime = 0;
4724 private float mTrackballRemainsX = 0.0f;
4725 private float mTrackballRemainsY = 0.0f;
4726 private int mTrackballXMove = 0;
4727 private int mTrackballYMove = 0;
4728 private boolean mExtendSelection = false;
4729 private boolean mTouchSelection = false;
4730 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
4731 private static final int TRACKBALL_TIMEOUT = 200;
4732 private static final int TRACKBALL_WAIT = 100;
4733 private static final int TRACKBALL_SCALE = 400;
4734 private static final int TRACKBALL_SCROLL_COUNT = 5;
4735 private static final int TRACKBALL_MOVE_COUNT = 10;
4736 private static final int TRACKBALL_MULTIPLIER = 3;
4737 private static final int SELECT_CURSOR_OFFSET = 16;
4738 private int mSelectX = 0;
4739 private int mSelectY = 0;
Cary Clark5da9aeb2009-10-06 17:40:53 -04004740 private boolean mFocusSizeChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004741 private boolean mShiftIsPressed = false;
4742 private boolean mTrackballDown = false;
4743 private long mTrackballUpTime = 0;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004744 private long mLastCursorTime = 0;
4745 private Rect mLastCursorBounds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004746
4747 // Set by default; BrowserActivity clears to interpret trackball data
Cary Clarkd6982c92009-05-29 11:02:22 -04004748 // directly for movement. Currently, the framework only passes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004749 // arrow key events, not trackball events, from one child to the next
4750 private boolean mMapTrackballToArrowKeys = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004751
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004752 public void setMapTrackballToArrowKeys(boolean setMap) {
4753 mMapTrackballToArrowKeys = setMap;
4754 }
4755
4756 void resetTrackballTime() {
4757 mTrackballLastTime = 0;
4758 }
4759
4760 @Override
4761 public boolean onTrackballEvent(MotionEvent ev) {
4762 long time = ev.getEventTime();
4763 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
4764 if (ev.getY() > 0) pageDown(true);
4765 if (ev.getY() < 0) pageUp(true);
4766 return true;
4767 }
4768 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Cary Clarkbadd8392009-10-15 13:32:08 -04004769 if (mShiftIsPressed) {
4770 return true; // discard press if copy in progress
4771 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004772 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004773 if (mNativeClass == 0) {
4774 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004775 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004776 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004777 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
4778 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
4779 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004780 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004781 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004782 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004783 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004784 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004785 }
4786 if (isInTouchMode()) requestFocusFromTouch();
4787 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04004788 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004789 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04004790 // LONG_PRESS_CENTER is set in common onKeyDown
4791 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004792 mTrackballDown = false;
4793 mTrackballUpTime = time;
4794 if (mShiftIsPressed) {
4795 if (mExtendSelection) {
4796 commitCopy();
4797 } else {
4798 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04004799 invalidate(); // draw the i-beam instead of the arrow
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004800 }
Cary Clarkbadd8392009-10-15 13:32:08 -04004801 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004802 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004803 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004804 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004805 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004806 );
4807 }
4808 return false; // let common code in onKeyUp at it
4809 }
4810 if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004811 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004812 return false;
4813 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004814 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004815 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004816 return true; // discard move if trackball is down
4817 }
4818 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004819 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004820 return true;
4821 }
4822 // TODO: alternatively we can do panning as touch does
4823 switchOutDrawHistory();
4824 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004825 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004826 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004827 + time + " last=" + mTrackballLastTime);
4828 }
4829 mTrackballFirstTime = time;
4830 mTrackballXMove = mTrackballYMove = 0;
4831 }
4832 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004833 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004834 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
4835 }
4836 mTrackballRemainsX += ev.getX();
4837 mTrackballRemainsY += ev.getY();
4838 doTrackball(time);
4839 return true;
4840 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004842 void moveSelection(float xRate, float yRate) {
4843 if (mNativeClass == 0)
4844 return;
4845 int width = getViewWidth();
4846 int height = getViewHeight();
Cary Clarkc05af372009-10-16 10:52:27 -04004847 mSelectX += xRate;
4848 mSelectY += yRate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004849 int maxX = width + mScrollX;
4850 int maxY = height + mScrollY;
4851 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
4852 , mSelectX));
4853 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
4854 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004855 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004856 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004857 + " mSelectX=" + mSelectX
4858 + " mSelectY=" + mSelectY
4859 + " mScrollX=" + mScrollX
4860 + " mScrollY=" + mScrollY
4861 + " xRate=" + xRate
4862 + " yRate=" + yRate
4863 );
4864 }
Leon Scroggins0236e672009-09-02 21:12:08 -04004865 nativeMoveSelection(viewToContentX(mSelectX),
4866 viewToContentY(mSelectY), mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004867 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004868 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004869 : 0;
4870 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004871 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004872 : 0;
4873 pinScrollBy(scrollX, scrollY, true, 0);
4874 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
4875 requestRectangleOnScreen(select);
4876 invalidate();
4877 }
4878
4879 private int scaleTrackballX(float xRate, int width) {
4880 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
4881 int nextXMove = xMove;
4882 if (xMove > 0) {
4883 if (xMove > mTrackballXMove) {
4884 xMove -= mTrackballXMove;
4885 }
4886 } else if (xMove < mTrackballXMove) {
4887 xMove -= mTrackballXMove;
4888 }
4889 mTrackballXMove = nextXMove;
4890 return xMove;
4891 }
4892
4893 private int scaleTrackballY(float yRate, int height) {
4894 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
4895 int nextYMove = yMove;
4896 if (yMove > 0) {
4897 if (yMove > mTrackballYMove) {
4898 yMove -= mTrackballYMove;
4899 }
4900 } else if (yMove < mTrackballYMove) {
4901 yMove -= mTrackballYMove;
4902 }
4903 mTrackballYMove = nextYMove;
4904 return yMove;
4905 }
4906
4907 private int keyCodeToSoundsEffect(int keyCode) {
4908 switch(keyCode) {
4909 case KeyEvent.KEYCODE_DPAD_UP:
4910 return SoundEffectConstants.NAVIGATION_UP;
4911 case KeyEvent.KEYCODE_DPAD_RIGHT:
4912 return SoundEffectConstants.NAVIGATION_RIGHT;
4913 case KeyEvent.KEYCODE_DPAD_DOWN:
4914 return SoundEffectConstants.NAVIGATION_DOWN;
4915 case KeyEvent.KEYCODE_DPAD_LEFT:
4916 return SoundEffectConstants.NAVIGATION_LEFT;
4917 }
4918 throw new IllegalArgumentException("keyCode must be one of " +
4919 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
4920 "KEYCODE_DPAD_LEFT}.");
4921 }
4922
4923 private void doTrackball(long time) {
4924 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
4925 if (elapsed == 0) {
4926 elapsed = TRACKBALL_TIMEOUT;
4927 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004928 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004929 float yRate = mTrackballRemainsY * 1000 / elapsed;
Cary Clarkc05af372009-10-16 10:52:27 -04004930 int viewWidth = getViewWidth();
4931 int viewHeight = getViewHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004932 if (mShiftIsPressed) {
Cary Clarkc05af372009-10-16 10:52:27 -04004933 moveSelection(scaleTrackballX(xRate, viewWidth),
4934 scaleTrackballY(yRate, viewHeight));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004935 mTrackballRemainsX = mTrackballRemainsY = 0;
4936 return;
4937 }
4938 float ax = Math.abs(xRate);
4939 float ay = Math.abs(yRate);
4940 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004941 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004942 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
4943 + " xRate=" + xRate
4944 + " yRate=" + yRate
4945 + " mTrackballRemainsX=" + mTrackballRemainsX
4946 + " mTrackballRemainsY=" + mTrackballRemainsY);
4947 }
Cary Clarkc05af372009-10-16 10:52:27 -04004948 int width = mContentWidth - viewWidth;
4949 int height = mContentHeight - viewHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004950 if (width < 0) width = 0;
4951 if (height < 0) height = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004952 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
4953 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
4954 maxA = Math.max(ax, ay);
4955 int count = Math.max(0, (int) maxA);
4956 int oldScrollX = mScrollX;
4957 int oldScrollY = mScrollY;
4958 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004959 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
4960 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004961 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
4962 KeyEvent.KEYCODE_DPAD_RIGHT;
4963 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004964 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004965 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004966 + " count=" + count
4967 + " mTrackballRemainsX=" + mTrackballRemainsX
4968 + " mTrackballRemainsY=" + mTrackballRemainsY);
4969 }
Cary Clark215b72c2009-06-26 14:38:43 -04004970 if (navHandledKey(selectKeyCode, count, false, time, false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004971 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
4972 }
4973 mTrackballRemainsX = mTrackballRemainsY = 0;
4974 }
4975 if (count >= TRACKBALL_SCROLL_COUNT) {
4976 int xMove = scaleTrackballX(xRate, width);
4977 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004978 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004979 Log.v(LOGTAG, "doTrackball pinScrollBy"
4980 + " count=" + count
4981 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04004982 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
4983 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004984 );
4985 }
4986 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
4987 xMove = 0;
4988 }
4989 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
4990 yMove = 0;
4991 }
4992 if (xMove != 0 || yMove != 0) {
4993 pinScrollBy(xMove, yMove, true, 0);
4994 }
4995 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004996 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004997 }
4998
Mike Reede8853fc2009-09-04 14:01:48 -04004999 private int computeMaxScrollY() {
Grace Klobae621d6f2009-09-11 13:20:39 -07005000 int maxContentH = computeVerticalScrollRange() + getTitleHeight();
Grace Klobabf5b6322009-11-10 15:34:34 -08005001 return Math.max(maxContentH - getViewHeightWithTitle(), getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04005002 }
5003
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005004 public void flingScroll(int vx, int vy) {
5005 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04005006 int maxY = computeMaxScrollY();
Cary Clarkd6982c92009-05-29 11:02:22 -04005007
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005008 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
5009 invalidate();
5010 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005011
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005012 private void doFling() {
5013 if (mVelocityTracker == null) {
5014 return;
5015 }
5016 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04005017 int maxY = computeMaxScrollY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005018
Romain Guy4296fc42009-07-06 11:48:52 -07005019 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005020 int vx = (int) mVelocityTracker.getXVelocity();
5021 int vy = (int) mVelocityTracker.getYVelocity();
5022
5023 if (mSnapScrollMode != SNAP_NONE) {
Cary Clarkac492e12009-10-14 14:53:37 -04005024 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005025 vy = 0;
5026 } else {
5027 vx = 0;
5028 }
5029 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005030
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005031 if (true /* EMG release: make our fling more like Maps' */) {
5032 // maps cuts their velocity in half
5033 vx = vx * 3 / 4;
5034 vy = vy * 3 / 4;
5035 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04005036 if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005037 WebViewCore.resumePriority();
Cary Clarkaa7caa62009-09-08 14:15:07 -04005038 return;
5039 }
Cary Clark278ce052009-08-31 16:08:42 -04005040 float currentVelocity = mScroller.getCurrVelocity();
5041 if (mLastVelocity > 0 && currentVelocity > 0) {
5042 float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
5043 - Math.atan2(vy, vx)));
5044 final float circle = (float) (Math.PI) * 2.0f;
5045 if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
5046 vx += currentVelocity * mLastVelX / mLastVelocity;
5047 vy += currentVelocity * mLastVelY / mLastVelocity;
5048 if (DebugFlags.WEB_VIEW) {
5049 Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
5050 }
5051 } else if (DebugFlags.WEB_VIEW) {
5052 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
5053 }
5054 } else if (DebugFlags.WEB_VIEW) {
5055 Log.v(LOGTAG, "doFling start last=" + mLastVelocity
Cary Clarkaa7caa62009-09-08 14:15:07 -04005056 + " current=" + currentVelocity
5057 + " vx=" + vx + " vy=" + vy
5058 + " maxX=" + maxX + " maxY=" + maxY
5059 + " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
Cary Clark278ce052009-08-31 16:08:42 -04005060 }
5061 mLastVelX = vx;
5062 mLastVelY = vy;
5063 mLastVelocity = (float) Math.hypot(vx, vy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005064
5065 mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
5066 // TODO: duration is calculated based on velocity, if the range is
5067 // small, the animation will stop before duration is up. We may
5068 // want to calculate how long the animation is going to run to precisely
5069 // resume the webcore update.
5070 final int time = mScroller.getDuration();
Grace Kloba96949ef2010-01-25 09:53:01 -08005071 mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_PRIORITY, time);
Mike Cleronf116bf82009-09-27 19:14:12 -07005072 awakenScrollBars(time);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005073 invalidate();
5074 }
5075
5076 private boolean zoomWithPreview(float scale) {
5077 float oldScale = mActualScale;
Grace Kloba675c7d22009-07-23 09:21:21 -07005078 mInitialScrollX = mScrollX;
5079 mInitialScrollY = mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005080
Grace Kloba25737912009-06-19 12:42:47 -07005081 // snap to DEFAULT_SCALE if it is close
Grace Kloba0d8b77c2009-06-25 11:20:51 -07005082 if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
5083 scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005084 }
5085
Grace Kloba3a0def22010-01-23 21:11:54 -08005086 setNewZoomScale(scale, true, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005087
5088 if (oldScale != mActualScale) {
5089 // use mZoomPickerScale to see zoom preview first
5090 mZoomStart = SystemClock.uptimeMillis();
5091 mInvInitialZoomScale = 1.0f / oldScale;
5092 mInvFinalZoomScale = 1.0f / mActualScale;
5093 mZoomScale = mActualScale;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005094 WebViewCore.pauseUpdatePicture(mWebViewCore);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005095 invalidate();
5096 return true;
5097 } else {
5098 return false;
5099 }
5100 }
5101
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005102 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005103 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
5104 * in charge of installing this view to the view hierarchy. This view will
5105 * become visible when the user starts scrolling via touch and fade away if
5106 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005107 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07005108 * API version 3 introduces a built-in zoom mechanism that is shown
5109 * automatically by the MapView. This is the preferred approach for
5110 * showing the zoom UI.
5111 *
5112 * @deprecated The built-in zoom mechanism is preferred, see
5113 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005114 */
The Android Open Source Project10592532009-03-18 17:39:46 -07005115 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005116 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07005117 if (!getSettings().supportZoom()) {
5118 Log.w(LOGTAG, "This WebView doesn't support zoom.");
5119 return null;
5120 }
5121 if (mZoomControls == null) {
5122 mZoomControls = createZoomControls();
Cary Clarkd6982c92009-05-29 11:02:22 -04005123
The Android Open Source Project10592532009-03-18 17:39:46 -07005124 /*
5125 * need to be set to VISIBLE first so that getMeasuredHeight() in
5126 * {@link #onSizeChanged()} can return the measured value for proper
5127 * layout.
5128 */
5129 mZoomControls.setVisibility(View.VISIBLE);
5130 mZoomControlRunnable = new Runnable() {
5131 public void run() {
Cary Clarkd6982c92009-05-29 11:02:22 -04005132
The Android Open Source Project10592532009-03-18 17:39:46 -07005133 /* Don't dismiss the controls if the user has
5134 * focus on them. Wait and check again later.
5135 */
5136 if (!mZoomControls.hasFocus()) {
5137 mZoomControls.hide();
5138 } else {
5139 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5140 mPrivateHandler.postDelayed(mZoomControlRunnable,
5141 ZOOM_CONTROLS_TIMEOUT);
5142 }
5143 }
5144 };
5145 }
5146 return mZoomControls;
5147 }
5148
5149 private ExtendedZoomControls createZoomControls() {
5150 ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
5151 , null);
5152 zoomControls.setOnZoomInClickListener(new OnClickListener() {
5153 public void onClick(View v) {
5154 // reset time out
5155 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5156 mPrivateHandler.postDelayed(mZoomControlRunnable,
5157 ZOOM_CONTROLS_TIMEOUT);
5158 zoomIn();
5159 }
5160 });
5161 zoomControls.setOnZoomOutClickListener(new OnClickListener() {
5162 public void onClick(View v) {
5163 // reset time out
5164 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5165 mPrivateHandler.postDelayed(mZoomControlRunnable,
5166 ZOOM_CONTROLS_TIMEOUT);
5167 zoomOut();
5168 }
5169 });
The Android Open Source Project10592532009-03-18 17:39:46 -07005170 return zoomControls;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005171 }
5172
5173 /**
5174 * Gets the {@link ZoomButtonsController} which can be used to add
5175 * additional buttons to the zoom controls window.
Cary Clarkd6982c92009-05-29 11:02:22 -04005176 *
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005177 * @return The instance of {@link ZoomButtonsController} used by this class,
5178 * or null if it is unavailable.
The Android Open Source Project10592532009-03-18 17:39:46 -07005179 * @hide
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005180 */
5181 public ZoomButtonsController getZoomButtonsController() {
5182 return mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005183 }
5184
5185 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005186 * Perform zoom in in the webview
5187 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
5188 */
5189 public boolean zoomIn() {
5190 // TODO: alternatively we can disallow this during draw history mode
5191 switchOutDrawHistory();
Grace Kloba3a0def22010-01-23 21:11:54 -08005192 mInZoomOverview = false;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005193 // Center zooming to the center of the screen.
Grace Kloba3a0def22010-01-23 21:11:54 -08005194 mZoomCenterX = getViewWidth() * .5f;
5195 mZoomCenterY = getViewHeight() * .5f;
5196 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5197 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
5198 return zoomWithPreview(mActualScale * 1.25f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005199 }
5200
5201 /**
5202 * Perform zoom out in the webview
5203 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
5204 */
5205 public boolean zoomOut() {
5206 // TODO: alternatively we can disallow this during draw history mode
5207 switchOutDrawHistory();
Grace Kloba3a0def22010-01-23 21:11:54 -08005208 // Center zooming to the center of the screen.
5209 mZoomCenterX = getViewWidth() * .5f;
5210 mZoomCenterY = getViewHeight() * .5f;
5211 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5212 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
5213 return zoomWithPreview(mActualScale * 0.8f);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005214 }
5215
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005216 private void updateSelection() {
5217 if (mNativeClass == 0) {
5218 return;
5219 }
5220 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04005221 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
5222 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005223 Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
5224 contentX + mNavSlop, contentY + mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005225 nativeSelectBestAt(rect);
5226 }
5227
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005228 /**
Leon Scroggins72543e12009-07-23 15:29:45 -04005229 * Scroll the focused text field/area to match the WebTextView
Cary Clarkeaa18de2009-09-28 12:50:42 -04005230 * @param xPercent New x position of the WebTextView from 0 to 1.
Leon Scroggins72543e12009-07-23 15:29:45 -04005231 * @param y New y position of the WebTextView in view coordinates
5232 */
Cary Clarkeaa18de2009-09-28 12:50:42 -04005233 /*package*/ void scrollFocusedTextInput(float xPercent, int y) {
Leon Scroggins72543e12009-07-23 15:29:45 -04005234 if (!inEditingMode() || mWebViewCore == null) {
5235 return;
5236 }
Cary Clarkeaa18de2009-09-28 12:50:42 -04005237 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT,
Leon Scrogginsd3997e52009-09-21 14:15:18 -04005238 // Since this position is relative to the top of the text input
5239 // field, we do not need to take the title bar's height into
5240 // consideration.
Cary Clarkeaa18de2009-09-28 12:50:42 -04005241 viewToContentDimension(y),
5242 new Float(xPercent));
Leon Scroggins72543e12009-07-23 15:29:45 -04005243 }
5244
5245 /**
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005246 * Set our starting point and time for a drag from the WebTextView.
5247 */
5248 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
5249 if (!inEditingMode()) {
5250 return;
5251 }
5252 mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
5253 mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
5254 mLastTouchTime = eventTime;
5255 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04005256 abortAnimation();
Grace Kloba96949ef2010-01-25 09:53:01 -08005257 mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005258 }
5259 mSnapScrollMode = SNAP_NONE;
5260 mVelocityTracker = VelocityTracker.obtain();
5261 mTouchMode = TOUCH_DRAG_START_MODE;
5262 }
5263
5264 /**
5265 * Given a motion event from the WebTextView, set its location to our
5266 * coordinates, and handle the event.
5267 */
5268 /*package*/ boolean textFieldDrag(MotionEvent event) {
5269 if (!inEditingMode()) {
5270 return false;
5271 }
Leon Scroggins72543e12009-07-23 15:29:45 -04005272 mDragFromTextInput = true;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005273 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
5274 (float) (mWebTextView.getTop() - mScrollY));
Leon Scroggins72543e12009-07-23 15:29:45 -04005275 boolean result = onTouchEvent(event);
5276 mDragFromTextInput = false;
5277 return result;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005278 }
5279
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005280 /**
Leon Scrogginsf90b1262009-11-24 14:49:21 -05005281 * Due a touch up from a WebTextView. This will be handled by webkit to
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005282 * change the selection.
5283 * @param event MotionEvent in the WebTextView's coordinates.
5284 */
5285 /*package*/ void touchUpOnTextField(MotionEvent event) {
5286 if (!inEditingMode()) {
5287 return;
5288 }
Leon Scroggins0236e672009-09-02 21:12:08 -04005289 int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
5290 int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
Leon Scrogginsf90b1262009-11-24 14:49:21 -05005291 nativeMotionUp(x, y, mNavSlop);
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005292 }
5293
Leon Scroggins1d96ca02009-10-23 11:49:03 -04005294 /**
5295 * Called when pressing the center key or trackball on a textfield.
5296 */
5297 /*package*/ void centerKeyPressOnTextField() {
5298 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
5299 nativeCursorNodePointer());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005300 }
5301
5302 private void doShortPress() {
5303 if (mNativeClass == 0) {
5304 return;
5305 }
5306 switchOutDrawHistory();
5307 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04005308 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
5309 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
Cary Clark1cb97ee2009-12-11 12:10:36 -05005310 if (nativePointInNavCache(contentX, contentY, mNavSlop)) {
5311 WebViewCore.MotionUpData motionUpData = new WebViewCore
5312 .MotionUpData();
5313 motionUpData.mFrame = nativeCacheHitFramePointer();
5314 motionUpData.mNode = nativeCacheHitNodePointer();
5315 motionUpData.mBounds = nativeCacheHitNodeBounds();
5316 motionUpData.mX = contentX;
5317 motionUpData.mY = contentY;
5318 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
5319 motionUpData);
5320 } else {
Cary Clarkbad0c542010-01-11 14:58:21 -05005321 doMotionUp(contentX, contentY);
Cary Clark1cb97ee2009-12-11 12:10:36 -05005322 }
5323 }
5324
Cary Clarkbad0c542010-01-11 14:58:21 -05005325 private void doMotionUp(int contentX, int contentY) {
5326 if (nativeMotionUp(contentX, contentY, mNavSlop)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005327 if (mLogEvent) {
5328 Checkin.updateStats(mContext.getContentResolver(),
5329 Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
5330 }
5331 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005332 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005333 playSoundEffect(SoundEffectConstants.CLICK);
5334 }
5335 }
5336
Grace Kloba3a0def22010-01-23 21:11:54 -08005337 // Rule for double tap:
5338 // 1. if the current scale is not same as the text wrap scale and layout
5339 // algorithm is NARROW_COLUMNS, fit to column;
5340 // 2. if the current state is not overview mode, change to overview mode;
5341 // 3. if the current state is overview mode, change to default scale.
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005342 private void doDoubleTap() {
Leon Scroggins0236e672009-09-02 21:12:08 -04005343 if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005344 return;
5345 }
5346 mZoomCenterX = mLastTouchX;
5347 mZoomCenterY = mLastTouchY;
Grace Kloba3a0def22010-01-23 21:11:54 -08005348 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5349 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Grace Klobaf8d8b462009-09-20 15:57:49 -07005350 WebSettings settings = getSettings();
Grace Kloba3a0def22010-01-23 21:11:54 -08005351 // remove the zoom control after double tap
Grace Klobaf8d8b462009-09-20 15:57:49 -07005352 if (settings.getBuiltInZoomControls()) {
Grace Klobad7660cc2009-08-17 17:13:01 -07005353 if (mZoomButtonsController.isVisible()) {
5354 mZoomButtonsController.setVisible(false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005355 }
Grace Klobad7660cc2009-08-17 17:13:01 -07005356 } else {
5357 if (mZoomControlRunnable != null) {
5358 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5359 }
5360 if (mZoomControls != null) {
5361 mZoomControls.hide();
5362 }
5363 }
Grace Klobaf8d8b462009-09-20 15:57:49 -07005364 settings.setDoubleTapToastCount(0);
Grace Kloba3a0def22010-01-23 21:11:54 -08005365 boolean zoomToDefault = false;
5366 if ((settings.getLayoutAlgorithm() == WebSettings.LayoutAlgorithm.NARROW_COLUMNS)
5367 && (Math.abs(mActualScale - mTextWrapScale) >= 0.01f)) {
5368 setNewZoomScale(mActualScale, true, true);
5369 float overviewScale = (float) getViewWidth() / mZoomOverviewWidth;
5370 if (Math.abs(mActualScale - overviewScale) < 0.01f) {
5371 mInZoomOverview = true;
5372 }
5373 } else if (!mInZoomOverview) {
Grace Kloba86773fb2009-11-19 11:25:20 -08005374 float newScale = (float) getViewWidth() / mZoomOverviewWidth;
Grace Kloba3a0def22010-01-23 21:11:54 -08005375 if (Math.abs(mActualScale - newScale) >= 0.01f) {
5376 mInZoomOverview = true;
Grace Kloba86773fb2009-11-19 11:25:20 -08005377 // Force the titlebar fully reveal in overview mode
5378 if (mScrollY < getTitleHeight()) mScrollY = 0;
5379 zoomWithPreview(newScale);
Grace Kloba3a0def22010-01-23 21:11:54 -08005380 } else if (Math.abs(mActualScale - mDefaultScale) >= 0.01f) {
5381 zoomToDefault = true;
Grace Kloba86773fb2009-11-19 11:25:20 -08005382 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005383 } else {
Grace Kloba3a0def22010-01-23 21:11:54 -08005384 zoomToDefault = true;
5385 }
5386 if (zoomToDefault) {
5387 mInZoomOverview = false;
5388 int left = nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005389 if (left != NO_LEFTEDGE) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005390 // add a 5pt padding to the left edge.
5391 int viewLeft = contentToViewX(left < 5 ? 0 : (left - 5))
5392 - mScrollX;
5393 // Re-calculate the zoom center so that the new scroll x will be
5394 // on the left edge.
5395 if (viewLeft > 0) {
5396 mZoomCenterX = viewLeft * mDefaultScale
5397 / (mDefaultScale - mActualScale);
5398 } else {
5399 scrollBy(viewLeft, 0);
5400 mZoomCenterX = 0;
5401 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005402 }
Grace Kloba3a0def22010-01-23 21:11:54 -08005403 zoomWithPreview(mDefaultScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005404 }
5405 }
5406
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005407 // Called by JNI to handle a touch on a node representing an email address,
5408 // address, or phone number
5409 private void overrideLoading(String url) {
5410 mCallbackProxy.uiOverrideUrlLoading(url);
5411 }
5412
5413 @Override
5414 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
5415 boolean result = false;
5416 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005417 result = mWebTextView.requestFocus(direction,
5418 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005419 } else {
5420 result = super.requestFocus(direction, previouslyFocusedRect);
5421 if (mWebViewCore.getSettings().getNeedInitialFocus()) {
5422 // For cases such as GMail, where we gain focus from a direction,
5423 // we want to move to the first available link.
5424 // FIXME: If there are no visible links, we may not want to
5425 int fakeKeyDirection = 0;
5426 switch(direction) {
5427 case View.FOCUS_UP:
5428 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
5429 break;
5430 case View.FOCUS_DOWN:
5431 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
5432 break;
5433 case View.FOCUS_LEFT:
5434 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
5435 break;
5436 case View.FOCUS_RIGHT:
5437 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
5438 break;
5439 default:
5440 return result;
5441 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005442 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Cary Clark215b72c2009-06-26 14:38:43 -04005443 navHandledKey(fakeKeyDirection, 1, true, 0, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005444 }
5445 }
5446 }
5447 return result;
5448 }
5449
5450 @Override
5451 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
5452 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
5453
5454 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
5455 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
5456 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
5457 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
5458
5459 int measuredHeight = heightSize;
5460 int measuredWidth = widthSize;
5461
5462 // Grab the content size from WebViewCore.
Grace Klobae621d6f2009-09-11 13:20:39 -07005463 int contentHeight = contentToViewDimension(mContentHeight);
5464 int contentWidth = contentToViewDimension(mContentWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005465
5466// Log.d(LOGTAG, "------- measure " + heightMode);
5467
5468 if (heightMode != MeasureSpec.EXACTLY) {
5469 mHeightCanMeasure = true;
5470 measuredHeight = contentHeight;
5471 if (heightMode == MeasureSpec.AT_MOST) {
5472 // If we are larger than the AT_MOST height, then our height can
5473 // no longer be measured and we should scroll internally.
5474 if (measuredHeight > heightSize) {
5475 measuredHeight = heightSize;
5476 mHeightCanMeasure = false;
5477 }
5478 }
5479 } else {
5480 mHeightCanMeasure = false;
5481 }
5482 if (mNativeClass != 0) {
5483 nativeSetHeightCanMeasure(mHeightCanMeasure);
5484 }
5485 // For the width, always use the given size unless unspecified.
5486 if (widthMode == MeasureSpec.UNSPECIFIED) {
5487 mWidthCanMeasure = true;
5488 measuredWidth = contentWidth;
5489 } else {
5490 mWidthCanMeasure = false;
5491 }
5492
5493 synchronized (this) {
5494 setMeasuredDimension(measuredWidth, measuredHeight);
5495 }
5496 }
5497
5498 @Override
5499 public boolean requestChildRectangleOnScreen(View child,
5500 Rect rect,
5501 boolean immediate) {
5502 rect.offset(child.getLeft() - child.getScrollX(),
5503 child.getTop() - child.getScrollY());
5504
Grace Kloba8eff73f2009-09-24 09:34:32 -07005505 int height = getViewHeightWithTitle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005506 int screenTop = mScrollY;
5507 int screenBottom = screenTop + height;
5508
5509 int scrollYDelta = 0;
5510
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005511 if (rect.bottom > screenBottom) {
5512 int oneThirdOfScreenHeight = height / 3;
5513 if (rect.height() > 2 * oneThirdOfScreenHeight) {
5514 // If the rectangle is too tall to fit in the bottom two thirds
5515 // of the screen, place it at the top.
5516 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005517 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005518 // If the rectangle will still fit on screen, we want its
5519 // top to be in the top third of the screen.
5520 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005521 }
5522 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005523 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005524 }
5525
5526 int width = getWidth() - getVerticalScrollbarWidth();
5527 int screenLeft = mScrollX;
5528 int screenRight = screenLeft + width;
5529
5530 int scrollXDelta = 0;
5531
5532 if (rect.right > screenRight && rect.left > screenLeft) {
5533 if (rect.width() > width) {
5534 scrollXDelta += (rect.left - screenLeft);
5535 } else {
5536 scrollXDelta += (rect.right - screenRight);
5537 }
5538 } else if (rect.left < screenLeft) {
5539 scrollXDelta -= (screenLeft - rect.left);
5540 }
5541
5542 if ((scrollYDelta | scrollXDelta) != 0) {
5543 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
5544 }
5545
5546 return false;
5547 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005548
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005549 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
5550 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04005551 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
5552 arg.mReplace = replace;
5553 arg.mNewStart = newStart;
5554 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07005555 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04005556 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005557 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
5558 }
5559
5560 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkded054c2009-06-15 10:26:08 -04005561 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
5562 arg.mEvent = event;
5563 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005564 // Increase our text generation number, and pass it to webcore thread
5565 mTextGeneration++;
5566 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
5567 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04005568 // To make sure the host application, like Browser, has the up to date
5569 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005570 // document state.
5571 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
5572 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04005573 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005574 }
5575
5576 /* package */ WebViewCore getWebViewCore() {
5577 return mWebViewCore;
5578 }
5579
5580 //-------------------------------------------------------------------------
5581 // Methods can be called from a separate thread, like WebViewCore
5582 // If it needs to call the View system, it has to send message.
5583 //-------------------------------------------------------------------------
5584
5585 /**
5586 * General handler to receive message coming from webkit thread
5587 */
5588 class PrivateHandler extends Handler {
5589 @Override
5590 public void handleMessage(Message msg) {
Cary Clark3e88ddc2009-10-08 14:59:46 -04005591 // exclude INVAL_RECT_MSG_ID since it is frequently output
5592 if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005593 Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
Jean-Baptiste Querud09f4aa2010-01-28 15:14:57 -08005594 > FIND_AGAIN ? Integer.toString(msg.what)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005595 : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
5596 }
Grace Kloba207308a2009-09-27 11:44:14 -07005597 if (mWebViewCore == null) {
5598 // after WebView's destroy() is called, skip handling messages.
5599 return;
5600 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005601 switch (msg.what) {
5602 case REMEMBER_PASSWORD: {
5603 mDatabase.setUsernamePassword(
5604 msg.getData().getString("host"),
5605 msg.getData().getString("username"),
5606 msg.getData().getString("password"));
5607 ((Message) msg.obj).sendToTarget();
5608 break;
5609 }
5610 case NEVER_REMEMBER_PASSWORD: {
5611 mDatabase.setUsernamePassword(
5612 msg.getData().getString("host"), null, null);
5613 ((Message) msg.obj).sendToTarget();
5614 break;
5615 }
5616 case SWITCH_TO_SHORTPRESS: {
Grace Klobaf58af622009-09-24 17:41:23 -07005617 // if mPreventDrag is not confirmed, treat it as no so that
5618 // it won't block panning the page.
5619 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
5620 mPreventDrag = PREVENT_DRAG_NO;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005621 mPreventLongPress = false;
5622 mPreventDoubleTap = false;
Grace Klobaf58af622009-09-24 17:41:23 -07005623 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005624 if (mTouchMode == TOUCH_INIT_MODE) {
Grace Kloba11438c32009-12-16 11:39:12 -08005625 mTouchMode = mFullScreenHolder == null
5626 ? TOUCH_SHORTPRESS_START_MODE
5627 : TOUCH_SHORTPRESS_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005628 updateSelection();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005629 } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
5630 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005631 }
5632 break;
5633 }
5634 case SWITCH_TO_LONGPRESS: {
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005635 if (mPreventLongPress) {
5636 mTouchMode = TOUCH_DONE_MODE;
5637 WebViewCore.TouchEventData ted
5638 = new WebViewCore.TouchEventData();
5639 ted.mAction = WebViewCore.ACTION_LONGPRESS;
5640 ted.mX = viewToContentX((int) mLastTouchX + mScrollX);
5641 ted.mY = viewToContentY((int) mLastTouchY + mScrollY);
5642 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
5643 } else if (mPreventDrag == PREVENT_DRAG_NO) {
Grace Kloba6c451b72009-06-25 12:25:30 -07005644 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba11438c32009-12-16 11:39:12 -08005645 if (mFullScreenHolder == null) {
5646 performLongClick();
5647 rebuildWebTextView();
5648 }
Grace Kloba6c451b72009-06-25 12:25:30 -07005649 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005650 break;
5651 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005652 case RELEASE_SINGLE_TAP: {
Grace Klobacaf0ce32010-01-25 18:36:48 -08005653 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
5654 // if mPreventDrag is not confirmed, treat it as
5655 // no so that it won't block tap.
5656 mPreventDrag = PREVENT_DRAG_NO;
5657 mPreventLongPress = false;
5658 mPreventDoubleTap = false;
5659 }
Grace Klobaf58af622009-09-24 17:41:23 -07005660 if (mPreventDrag == PREVENT_DRAG_NO) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005661 mTouchMode = TOUCH_DONE_MODE;
5662 doShortPress();
5663 }
5664 break;
5665 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005666 case SCROLL_BY_MSG_ID:
5667 setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
5668 break;
5669 case SYNC_SCROLL_TO_MSG_ID:
5670 if (mUserScroll) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005671 // if user has scrolled explicitly, don't sync the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005672 // scroll position any more
5673 mUserScroll = false;
5674 break;
5675 }
5676 // fall through
5677 case SCROLL_TO_MSG_ID:
5678 if (setContentScrollTo(msg.arg1, msg.arg2)) {
5679 // if we can't scroll to the exact position due to pin,
Cary Clarkd6982c92009-05-29 11:02:22 -04005680 // send a message to WebCore to re-scroll when we get a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005681 // new picture
5682 mUserScroll = false;
5683 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
5684 msg.arg1, msg.arg2);
5685 }
5686 break;
5687 case SPAWN_SCROLL_TO_MSG_ID:
5688 spawnContentScrollTo(msg.arg1, msg.arg2);
5689 break;
Grace Kloba769ed212010-01-27 10:52:47 -08005690 case UPDATE_ZOOM_RANGE: {
5691 WebViewCore.RestoreState restoreState
5692 = (WebViewCore.RestoreState) msg.obj;
5693 // mScrollX contains the new minPrefWidth
5694 updateZoomRange(restoreState, getViewWidth(),
5695 restoreState.mScrollX, false);
5696 break;
5697 }
Grace Klobae397a882009-08-06 12:04:14 -07005698 case NEW_PICTURE_MSG_ID: {
5699 WebSettings settings = mWebViewCore.getSettings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005700 // called for new content
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005701 final int viewWidth = getViewWidth();
Cary Clarkd6982c92009-05-29 11:02:22 -04005702 final WebViewCore.DrawData draw =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005703 (WebViewCore.DrawData) msg.obj;
5704 final Point viewSize = draw.mViewPoint;
Grace Klobae397a882009-08-06 12:04:14 -07005705 boolean useWideViewport = settings.getUseWideViewPort();
Grace Klobaef347ef2009-07-30 11:20:32 -07005706 WebViewCore.RestoreState restoreState = draw.mRestoreState;
Grace Kloba9a67c822009-12-20 11:33:58 -08005707 boolean hasRestoreState = restoreState != null;
5708 if (hasRestoreState) {
Grace Klobaef347ef2009-07-30 11:20:32 -07005709 mInZoomOverview = false;
Grace Kloba769ed212010-01-27 10:52:47 -08005710 updateZoomRange(restoreState, viewSize.x,
5711 draw.mMinPrefWidth, true);
Grace Kloba3a0def22010-01-23 21:11:54 -08005712 if (mInitialScaleInPercent > 0) {
5713 setNewZoomScale(mInitialScaleInPercent / 100.0f,
5714 mInitialScaleInPercent != mTextWrapScale * 100,
5715 false);
5716 } else if (restoreState.mViewScale > 0) {
5717 mTextWrapScale = restoreState.mTextWrapScale;
5718 setNewZoomScale(restoreState.mViewScale, false,
5719 false);
5720 } else {
5721 mInZoomOverview = useWideViewport
5722 && settings.getLoadWithOverviewMode();
5723 float scale;
5724 if (mInZoomOverview) {
5725 scale = (float) viewWidth
5726 / DEFAULT_VIEWPORT_WIDTH;
5727 } else {
5728 scale = restoreState.mTextWrapScale;
5729 }
5730 setNewZoomScale(scale, Math.abs(scale
5731 - mTextWrapScale) >= 0.01f, false);
5732 }
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04005733 setContentScrollTo(restoreState.mScrollX,
5734 restoreState.mScrollY);
Grace Klobaef347ef2009-07-30 11:20:32 -07005735 // As we are on a new page, remove the WebTextView. This
5736 // is necessary for page loads driven by webkit, and in
5737 // particular when the user was on a password field, so
5738 // the WebTextView was visible.
5739 clearTextEntry();
Mike Reed8b302092009-11-12 12:50:20 -05005740 // update the zoom buttons as the scale can be changed
5741 if (getSettings().getBuiltInZoomControls()) {
5742 updateZoomButtonsEnabled();
5743 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005744 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005745 // We update the layout (i.e. request a layout from the
5746 // view system) if the last view size that we sent to
5747 // WebCore matches the view size of the picture we just
5748 // received in the fixed dimension.
5749 final boolean updateLayout = viewSize.x == mLastWidthSent
5750 && viewSize.y == mLastHeightSent;
Cary Clarkd6982c92009-05-29 11:02:22 -04005751 recordNewContentSize(draw.mWidthHeight.x,
Cary Clark5bb6b522009-09-21 11:58:31 -04005752 draw.mWidthHeight.y
5753 + (mFindIsUp ? mFindHeight : 0), updateLayout);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005754 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005755 Rect b = draw.mInvalRegion.getBounds();
5756 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
5757 b.left+","+b.top+","+b.right+","+b.bottom+"}");
5758 }
Mike Reede9e86b82009-09-15 11:26:53 -04005759 invalidateContentRect(draw.mInvalRegion.getBounds());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005760 if (mPictureListener != null) {
5761 mPictureListener.onNewPicture(WebView.this, capturePicture());
5762 }
Grace Klobaef347ef2009-07-30 11:20:32 -07005763 if (useWideViewport) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005764 // limit mZoomOverviewWidth upper bound to
5765 // sMaxViewportWidth so that if the page doesn't behave
5766 // well, the WebView won't go insane. limit the lower
5767 // bound to match the default scale for mobile sites.
Grace Klobaa4fa1072009-11-09 12:01:50 -08005768 mZoomOverviewWidth = Math.min(sMaxViewportWidth, Math
Grace Kloba3a0def22010-01-23 21:11:54 -08005769 .max((int) (viewWidth / mDefaultScale), Math
5770 .max(draw.mMinPrefWidth,
5771 draw.mViewPoint.x)));
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005772 }
5773 if (!mMinZoomScaleFixed) {
Grace Klobae397a882009-08-06 12:04:14 -07005774 mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005775 }
5776 if (!mDrawHistory && mInZoomOverview) {
5777 // fit the content width to the current view. Ignore
5778 // the rounding error case.
5779 if (Math.abs((viewWidth * mInvActualScale)
5780 - mZoomOverviewWidth) > 1) {
Grace Klobae397a882009-08-06 12:04:14 -07005781 setNewZoomScale((float) viewWidth
Grace Kloba3a0def22010-01-23 21:11:54 -08005782 / mZoomOverviewWidth, Math.abs(mActualScale
5783 - mTextWrapScale) < 0.01f, false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005784 }
5785 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04005786 if (draw.mFocusSizeChanged && inEditingMode()) {
5787 mFocusSizeChanged = true;
5788 }
Grace Kloba9a67c822009-12-20 11:33:58 -08005789 if (hasRestoreState) {
5790 mViewManager.postReadyToDrawAll();
5791 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005792 break;
Grace Klobae397a882009-08-06 12:04:14 -07005793 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005794 case WEBCORE_INITIALIZED_MSG_ID:
5795 // nativeCreate sets mNativeClass to a non-zero value
5796 nativeCreate(msg.arg1);
5797 break;
5798 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
5799 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04005800 // and representing the same node as the pointer.
5801 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005802 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005803 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005804 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005805 int start = Selection.getSelectionStart(text);
5806 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005807 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005808 // Restore the selection, which may have been
5809 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005810 Spannable pword =
5811 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005812 Selection.setSelection(pword, start, end);
5813 // If the text entry has created more events, ignore
5814 // this one.
5815 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005816 mWebTextView.setTextAndKeepSelection(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005817 (String) msg.obj);
5818 }
5819 }
5820 break;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005821 case UPDATE_TEXT_SELECTION_MSG_ID:
5822 if (inEditingMode()
5823 && mWebTextView.isSameTextField(msg.arg1)
5824 && msg.arg2 == mTextGeneration) {
5825 WebViewCore.TextSelectionData tData
5826 = (WebViewCore.TextSelectionData) msg.obj;
5827 mWebTextView.setSelectionFromWebKit(tData.mStart,
5828 tData.mEnd);
5829 }
5830 break;
Leon Scroggins3a503392010-01-06 17:04:38 -05005831 case RETURN_LABEL:
5832 if (inEditingMode()
5833 && mWebTextView.isSameTextField(msg.arg1)) {
5834 mWebTextView.setHint((String) msg.obj);
5835 InputMethodManager imm
5836 = InputMethodManager.peekInstance();
5837 // The hint is propagated to the IME in
5838 // onCreateInputConnection. If the IME is already
5839 // active, restart it so that its hint text is updated.
5840 if (imm != null && imm.isActive(mWebTextView)) {
5841 imm.restartInput(mWebTextView);
5842 }
5843 }
5844 break;
Cary Clark215b72c2009-06-26 14:38:43 -04005845 case MOVE_OUT_OF_PLUGIN:
Derek Sollenberger718d69f2009-10-19 15:56:43 -04005846 navHandledKey(msg.arg1, 1, false, 0, true);
Cary Clark215b72c2009-06-26 14:38:43 -04005847 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005848 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04005849 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005850 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04005851 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005852 mWebTextView.bringIntoView();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005853 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005854 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005855 break;
Cary Clark243ea062009-06-25 10:49:32 -04005856 case CLEAR_TEXT_ENTRY:
5857 clearTextEntry();
5858 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005859 case INVAL_RECT_MSG_ID: {
5860 Rect r = (Rect)msg.obj;
5861 if (r == null) {
5862 invalidate();
5863 } else {
5864 // we need to scale r from content into view coords,
5865 // which viewInvalidate() does for us
5866 viewInvalidate(r.left, r.top, r.right, r.bottom);
5867 }
5868 break;
5869 }
Nicolas Roard38863332010-01-04 19:30:55 +00005870 case IMMEDIATE_REPAINT_MSG_ID: {
5871 int updates = msg.arg1;
5872 if (updates != 0) {
5873 // updates is a C++ pointer to a Vector of
5874 // AnimationValues that we apply to the layers.
5875 // The Vector is deallocated in nativeUpdateLayers().
Nicolas Roard493bcdd2010-01-19 21:08:49 +00005876 nativeUpdateLayers(updates);
Nicolas Roard38863332010-01-04 19:30:55 +00005877 }
5878 invalidate();
5879 break;
5880 }
5881 case SET_ROOT_LAYER_MSG_ID: {
5882 int oldLayer = mRootLayer;
5883 mRootLayer = msg.arg1;
5884 if (oldLayer > 0) {
5885 nativeDestroyLayer(oldLayer);
5886 }
5887 if (mRootLayer == 0) {
5888 mLayersHaveAnimations = false;
5889 }
5890 if (mEvaluateThread != null) {
5891 mEvaluateThread.cancel();
5892 mEvaluateThread = null;
5893 }
5894 if (nativeLayersHaveAnimations(mRootLayer)) {
5895 mLayersHaveAnimations = true;
5896 mEvaluateThread = new EvaluateLayersAnimations();
5897 mEvaluateThread.start();
5898 }
5899 invalidate();
5900 break;
5901 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005902 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04005903 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005904 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005905 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005906 }
5907 break;
Grace Kloba96949ef2010-01-25 09:53:01 -08005908 case RESUME_WEBCORE_PRIORITY:
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005909 WebViewCore.resumePriority();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005910 break;
5911
Leon Scrogginse3225672009-06-03 15:53:13 -04005912 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005913 // as this is shared by keydown and trackballdown, reset all
5914 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04005915 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005916 mTrackballDown = false;
Grace Kloba98e6fcf2010-01-27 15:20:30 -08005917 performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005918 break;
5919
5920 case WEBCORE_NEED_TOUCH_EVENTS:
5921 mForwardTouchEvents = (msg.arg1 != 0);
5922 break;
5923
5924 case PREVENT_TOUCH_ID:
5925 if (msg.arg1 == MotionEvent.ACTION_DOWN) {
Grace Klobaf58af622009-09-24 17:41:23 -07005926 // dont override if mPreventDrag has been set to no due
5927 // to time out
5928 if (mPreventDrag == PREVENT_DRAG_MAYBE_YES) {
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005929 mPreventDrag = (msg.arg2 & TOUCH_PREVENT_DRAG)
5930 == TOUCH_PREVENT_DRAG ? PREVENT_DRAG_YES
Grace Klobaf58af622009-09-24 17:41:23 -07005931 : PREVENT_DRAG_NO;
5932 if (mPreventDrag == PREVENT_DRAG_YES) {
5933 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005934 } else {
5935 mPreventLongPress =
5936 (msg.arg2 & TOUCH_PREVENT_LONGPRESS)
5937 == TOUCH_PREVENT_LONGPRESS;
5938 mPreventDoubleTap =
5939 (msg.arg2 & TOUCH_PREVENT_DOUBLETAP)
5940 == TOUCH_PREVENT_DOUBLETAP;
Grace Klobaf58af622009-09-24 17:41:23 -07005941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005942 }
5943 }
5944 break;
5945
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005946 case REQUEST_KEYBOARD:
5947 if (msg.arg1 == 0) {
5948 hideSoftKeyboard();
5949 } else {
Leon Scroggins04e0a102010-01-08 16:19:27 -05005950 displaySoftKeyboard(1 == msg.arg2);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005951 }
5952 break;
5953
Leon Scroggins5de63892009-10-29 09:48:43 -04005954 case FIND_AGAIN:
5955 // Ignore if find has been dismissed.
5956 if (mFindIsUp) {
5957 findAll(mLastFind);
5958 }
5959 break;
5960
Cary Clark25415e22009-10-12 13:41:28 -04005961 case DRAG_HELD_MOTIONLESS:
5962 mHeldMotionless = MOTIONLESS_TRUE;
5963 invalidate();
5964 // fall through to keep scrollbars awake
5965
5966 case AWAKEN_SCROLL_BARS:
5967 if (mTouchMode == TOUCH_DRAG_MODE
5968 && mHeldMotionless == MOTIONLESS_TRUE) {
5969 awakenScrollBars(ViewConfiguration
5970 .getScrollDefaultDelay(), false);
5971 mPrivateHandler.sendMessageDelayed(mPrivateHandler
5972 .obtainMessage(AWAKEN_SCROLL_BARS),
5973 ViewConfiguration.getScrollDefaultDelay());
5974 }
5975 break;
Cary Clark1cb97ee2009-12-11 12:10:36 -05005976
5977 case DO_MOTION_UP:
Cary Clarkbad0c542010-01-11 14:58:21 -05005978 doMotionUp(msg.arg1, msg.arg2);
Cary Clark1cb97ee2009-12-11 12:10:36 -05005979 break;
5980
Grace Kloba3a0def22010-01-23 21:11:54 -08005981 case SHOW_FULLSCREEN: {
Grace Kloba11438c32009-12-16 11:39:12 -08005982 WebViewCore.PluginFullScreenData data
5983 = (WebViewCore.PluginFullScreenData) msg.obj;
5984 if (data.mNpp != 0 && data.mView != null) {
5985 if (mFullScreenHolder != null) {
5986 Log.w(LOGTAG,
5987 "Should not have another full screen.");
5988 mFullScreenHolder.dismiss();
5989 }
5990 mFullScreenHolder = new PluginFullScreenHolder(
5991 WebView.this, data.mNpp);
Grace Kloba77dc1792010-01-21 19:13:34 -08005992 // as we are sharing the View between full screen and
5993 // embedded mode, we have to remove the
5994 // AbsoluteLayout.LayoutParams set by embedded mode to
5995 // ViewGroup.LayoutParams before adding it to the dialog
5996 data.mView.setLayoutParams(new ViewGroup.LayoutParams(
5997 ViewGroup.LayoutParams.FILL_PARENT,
5998 ViewGroup.LayoutParams.FILL_PARENT));
Grace Kloba11438c32009-12-16 11:39:12 -08005999 mFullScreenHolder.setContentView(data.mView);
6000 mFullScreenHolder.setCancelable(false);
6001 mFullScreenHolder.setCanceledOnTouchOutside(false);
Grace Kloba4350e142009-12-21 16:35:04 -08006002 mFullScreenHolder.show();
Grace Klobacaf0ce32010-01-25 18:36:48 -08006003 } else if (mFullScreenHolder == null) {
6004 // this may happen if user dismisses the fullscreen and
6005 // then the WebCore re-position message finally reached
6006 // the UI thread.
6007 break;
Grace Kloba11438c32009-12-16 11:39:12 -08006008 }
Grace Kloba4350e142009-12-21 16:35:04 -08006009 // move the matching embedded view fully into the view so
6010 // that touch will be valid instead of rejected due to out
6011 // of the visible bounds
6012 // TODO: do we need to preserve the original position and
6013 // scale so that we can revert it when leaving the full
6014 // screen mode?
6015 int x = contentToViewX(data.mDocX);
6016 int y = contentToViewY(data.mDocY);
6017 int width = contentToViewDimension(data.mDocWidth);
6018 int height = contentToViewDimension(data.mDocHeight);
6019 int viewWidth = getViewWidth();
6020 int viewHeight = getViewHeight();
6021 int newX = mScrollX;
6022 int newY = mScrollY;
6023 if (x < mScrollX) {
6024 newX = x + (width > viewWidth
6025 ? (width - viewWidth) / 2 : 0);
6026 } else if (x + width > mScrollX + viewWidth) {
6027 newX = x + width - viewWidth - (width > viewWidth
6028 ? (width - viewWidth) / 2 : 0);
6029 }
6030 if (y < mScrollY) {
6031 newY = y + (height > viewHeight
6032 ? (height - viewHeight) / 2 : 0);
6033 } else if (y + height > mScrollY + viewHeight) {
6034 newY = y + height - viewHeight - (height > viewHeight
6035 ? (height - viewHeight) / 2 : 0);
6036 }
6037 scrollTo(newX, newY);
6038 if (width > viewWidth || height > viewHeight) {
6039 mZoomCenterX = viewWidth * .5f;
6040 mZoomCenterY = viewHeight * .5f;
Grace Kloba3a0def22010-01-23 21:11:54 -08006041 // do not change text wrap scale so that there is no
6042 // reflow
Grace Kloba4350e142009-12-21 16:35:04 -08006043 setNewZoomScale(mActualScale
6044 / Math.max((float) width / viewWidth,
Grace Kloba3a0def22010-01-23 21:11:54 -08006045 (float) height / viewHeight), false,
6046 false);
Grace Kloba4350e142009-12-21 16:35:04 -08006047 }
6048 // Now update the bound
Grace Kloba11438c32009-12-16 11:39:12 -08006049 mFullScreenHolder.updateBound(contentToViewX(data.mDocX)
6050 - mScrollX, contentToViewY(data.mDocY) - mScrollY,
6051 contentToViewDimension(data.mDocWidth),
6052 contentToViewDimension(data.mDocHeight));
Grace Kloba3a0def22010-01-23 21:11:54 -08006053 }
Grace Kloba11438c32009-12-16 11:39:12 -08006054 break;
6055
6056 case HIDE_FULLSCREEN:
6057 if (mFullScreenHolder != null) {
6058 mFullScreenHolder.dismiss();
6059 mFullScreenHolder = null;
6060 }
6061 break;
6062
Leon Scrogginse26efa32009-12-15 16:38:45 -05006063 case DOM_FOCUS_CHANGED:
6064 if (inEditingMode()) {
6065 nativeClearCursor();
6066 rebuildWebTextView();
6067 }
6068 break;
6069
Grace Kloba3a0def22010-01-23 21:11:54 -08006070 case SHOW_RECT_MSG_ID: {
6071 WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
6072 int x = mScrollX;
6073 int left = contentToViewDimension(data.mLeft);
6074 int width = contentToViewDimension(data.mWidth);
6075 int maxWidth = contentToViewDimension(data.mContentWidth);
6076 int viewWidth = getViewWidth();
6077 if (width < viewWidth) {
6078 // center align
6079 x += left + width / 2 - mScrollX - viewWidth / 2;
6080 } else {
6081 x += (int) (left + data.mXPercentInDoc * width
6082 - mScrollX - data.mXPercentInView * viewWidth);
6083 }
6084 // use the passing content width to cap x as the current
6085 // mContentWidth may not be updated yet
6086 x = Math.max(0,
6087 (Math.min(maxWidth, x + viewWidth)) - viewWidth);
6088 int y = mScrollY;
6089 int top = contentToViewDimension(data.mTop);
6090 int height = contentToViewDimension(data.mHeight);
6091 int maxHeight = contentToViewDimension(data.mContentHeight);
6092 int viewHeight = getViewHeight();
6093 if (height < viewHeight) {
6094 // middle align
6095 y += top + height / 2 - mScrollY - viewHeight / 2;
6096 } else {
6097 y += (int) (top + data.mYPercentInDoc * height
6098 - mScrollY - data.mYPercentInView * viewHeight);
6099 }
6100 // use the passing content height to cap y as the current
6101 // mContentHeight may not be updated yet
6102 y = Math.max(0,
6103 (Math.min(maxHeight, y + viewHeight) - viewHeight));
6104 scrollTo(x, y);
6105 }
6106 break;
6107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006108 default:
6109 super.handleMessage(msg);
6110 break;
6111 }
6112 }
6113 }
6114
6115 // Class used to use a dropdown for a <select> element
6116 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006117 // Whether the listbox allows multiple selection.
6118 private boolean mMultiple;
6119 // Passed in to a list with multiple selection to tell
6120 // which items are selected.
6121 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04006122 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006123 // where the initial selection is.
6124 private int mSelection;
6125
6126 private Container[] mContainers;
6127
6128 // Need these to provide stable ids to my ArrayAdapter,
6129 // which normally does not have stable ids. (Bug 1250098)
6130 private class Container extends Object {
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006131 /**
6132 * Possible values for mEnabled. Keep in sync with OptionStatus in
6133 * WebViewCore.cpp
6134 */
6135 final static int OPTGROUP = -1;
6136 final static int OPTION_DISABLED = 0;
6137 final static int OPTION_ENABLED = 1;
6138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006139 String mString;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006140 int mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006141 int mId;
6142
6143 public String toString() {
6144 return mString;
6145 }
6146 }
6147
6148 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04006149 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006150 * and allow filtering.
6151 */
6152 private class MyArrayListAdapter extends ArrayAdapter<Container> {
6153 public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006154 super(context,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006155 multiple ? com.android.internal.R.layout.select_dialog_multichoice :
Cary Clarkd6982c92009-05-29 11:02:22 -04006156 com.android.internal.R.layout.select_dialog_singlechoice,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006157 objects);
6158 }
6159
6160 @Override
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006161 public View getView(int position, View convertView,
6162 ViewGroup parent) {
6163 // Always pass in null so that we will get a new CheckedTextView
6164 // Otherwise, an item which was previously used as an <optgroup>
6165 // element (i.e. has no check), could get used as an <option>
6166 // element, which needs a checkbox/radio, but it would not have
6167 // one.
6168 convertView = super.getView(position, null, parent);
6169 Container c = item(position);
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -04006170 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
6171 // ListView does not draw dividers between disabled and
6172 // enabled elements. Use a LinearLayout to provide dividers
6173 LinearLayout layout = new LinearLayout(mContext);
6174 layout.setOrientation(LinearLayout.VERTICAL);
6175 if (position > 0) {
6176 View dividerTop = new View(mContext);
6177 dividerTop.setBackgroundResource(
6178 android.R.drawable.divider_horizontal_bright);
6179 layout.addView(dividerTop);
6180 }
6181
6182 if (Container.OPTGROUP == c.mEnabled) {
6183 // Currently select_dialog_multichoice and
6184 // select_dialog_singlechoice are CheckedTextViews. If
6185 // that changes, the class cast will no longer be valid.
6186 Assert.assertTrue(
6187 convertView instanceof CheckedTextView);
6188 ((CheckedTextView) convertView).setCheckMarkDrawable(
6189 null);
6190 } else {
6191 // c.mEnabled == Container.OPTION_DISABLED
6192 // Draw the disabled element in a disabled state.
6193 convertView.setEnabled(false);
6194 }
6195
6196 layout.addView(convertView);
6197 if (position < getCount() - 1) {
6198 View dividerBottom = new View(mContext);
6199 dividerBottom.setBackgroundResource(
6200 android.R.drawable.divider_horizontal_bright);
6201 layout.addView(dividerBottom);
6202 }
6203 return layout;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006204 }
6205 return convertView;
6206 }
6207
6208 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006209 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04006210 // AdapterView's onChanged method uses this to determine whether
6211 // to restore the old state. Return false so that the old (out
6212 // of date) state does not replace the new, valid state.
6213 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006214 }
6215
6216 private Container item(int position) {
6217 if (position < 0 || position >= getCount()) {
6218 return null;
6219 }
6220 return (Container) getItem(position);
6221 }
6222
6223 @Override
6224 public long getItemId(int position) {
6225 Container item = item(position);
6226 if (item == null) {
6227 return -1;
6228 }
6229 return item.mId;
6230 }
6231
6232 @Override
6233 public boolean areAllItemsEnabled() {
6234 return false;
6235 }
6236
6237 @Override
6238 public boolean isEnabled(int position) {
6239 Container item = item(position);
6240 if (item == null) {
6241 return false;
6242 }
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006243 return Container.OPTION_ENABLED == item.mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006244 }
6245 }
6246
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006247 private InvokeListBox(String[] array, int[] enabled, int[] selected) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006248 mMultiple = true;
6249 mSelectedArray = selected;
6250
6251 int length = array.length;
6252 mContainers = new Container[length];
6253 for (int i = 0; i < length; i++) {
6254 mContainers[i] = new Container();
6255 mContainers[i].mString = array[i];
6256 mContainers[i].mEnabled = enabled[i];
6257 mContainers[i].mId = i;
6258 }
6259 }
6260
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006261 private InvokeListBox(String[] array, int[] enabled, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006262 mSelection = selection;
6263 mMultiple = false;
6264
6265 int length = array.length;
6266 mContainers = new Container[length];
6267 for (int i = 0; i < length; i++) {
6268 mContainers[i] = new Container();
6269 mContainers[i].mString = array[i];
6270 mContainers[i].mEnabled = enabled[i];
6271 mContainers[i].mId = i;
6272 }
6273 }
6274
Leon Scroggins3667ce42009-05-13 15:58:03 -04006275 /*
6276 * Whenever the data set changes due to filtering, this class ensures
6277 * that the checked item remains checked.
6278 */
6279 private class SingleDataSetObserver extends DataSetObserver {
6280 private long mCheckedId;
6281 private ListView mListView;
6282 private Adapter mAdapter;
6283
6284 /*
6285 * Create a new observer.
6286 * @param id The ID of the item to keep checked.
6287 * @param l ListView for getting and clearing the checked states
6288 * @param a Adapter for getting the IDs
6289 */
6290 public SingleDataSetObserver(long id, ListView l, Adapter a) {
6291 mCheckedId = id;
6292 mListView = l;
6293 mAdapter = a;
6294 }
6295
6296 public void onChanged() {
6297 // The filter may have changed which item is checked. Find the
6298 // item that the ListView thinks is checked.
6299 int position = mListView.getCheckedItemPosition();
6300 long id = mAdapter.getItemId(position);
6301 if (mCheckedId != id) {
6302 // Clear the ListView's idea of the checked item, since
6303 // it is incorrect
6304 mListView.clearChoices();
6305 // Search for mCheckedId. If it is in the filtered list,
6306 // mark it as checked
6307 int count = mAdapter.getCount();
6308 for (int i = 0; i < count; i++) {
6309 if (mAdapter.getItemId(i) == mCheckedId) {
6310 mListView.setItemChecked(i, true);
6311 break;
6312 }
6313 }
6314 }
6315 }
6316
6317 public void onInvalidate() {}
6318 }
6319
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006320 public void run() {
6321 final ListView listView = (ListView) LayoutInflater.from(mContext)
6322 .inflate(com.android.internal.R.layout.select_dialog, null);
Cary Clarkd6982c92009-05-29 11:02:22 -04006323 final MyArrayListAdapter adapter = new
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006324 MyArrayListAdapter(mContext, mContainers, mMultiple);
6325 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
6326 .setView(listView).setCancelable(true)
6327 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04006328
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006329 if (mMultiple) {
6330 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
6331 public void onClick(DialogInterface dialog, int which) {
6332 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04006333 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006334 adapter.getCount(), 0,
6335 listView.getCheckedItemPositions());
6336 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07006337 b.setNegativeButton(android.R.string.cancel,
6338 new DialogInterface.OnClickListener() {
6339 public void onClick(DialogInterface dialog, int which) {
6340 mWebViewCore.sendMessage(
6341 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
6342 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006343 }
6344 final AlertDialog dialog = b.create();
6345 listView.setAdapter(adapter);
6346 listView.setFocusableInTouchMode(true);
6347 // There is a bug (1250103) where the checks in a ListView with
6348 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04006349 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006350 // filtered. Do not allow filtering on multiple lists until
6351 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04006352
Leon Scroggins3667ce42009-05-13 15:58:03 -04006353 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006354 if (mMultiple) {
6355 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
6356 int length = mSelectedArray.length;
6357 for (int i = 0; i < length; i++) {
6358 listView.setItemChecked(mSelectedArray[i], true);
6359 }
6360 } else {
6361 listView.setOnItemClickListener(new OnItemClickListener() {
6362 public void onItemClick(AdapterView parent, View v,
6363 int position, long id) {
6364 mWebViewCore.sendMessage(
6365 EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
6366 dialog.dismiss();
6367 }
6368 });
6369 if (mSelection != -1) {
6370 listView.setSelection(mSelection);
6371 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
6372 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04006373 DataSetObserver observer = new SingleDataSetObserver(
6374 adapter.getItemId(mSelection), listView, adapter);
6375 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006376 }
6377 }
6378 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
6379 public void onCancel(DialogInterface dialog) {
6380 mWebViewCore.sendMessage(
6381 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
6382 }
6383 });
6384 dialog.show();
6385 }
6386 }
6387
6388 /*
6389 * Request a dropdown menu for a listbox with multiple selection.
6390 *
6391 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006392 * @param enabledArray State for each element in the list. See static
6393 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006394 * @param selectedArray Which positions are initally selected.
6395 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006396 void requestListBox(String[] array, int[] enabledArray, int[]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006397 selectedArray) {
6398 mPrivateHandler.post(
6399 new InvokeListBox(array, enabledArray, selectedArray));
6400 }
6401
Grace Kloba769ed212010-01-27 10:52:47 -08006402 private void updateZoomRange(WebViewCore.RestoreState restoreState,
6403 int viewWidth, int minPrefWidth, boolean updateZoomOverview) {
6404 if (restoreState.mMinScale == 0) {
6405 if (restoreState.mMobileSite) {
6406 if (minPrefWidth > Math.max(0, viewWidth)) {
6407 mMinZoomScale = (float) viewWidth / minPrefWidth;
6408 mMinZoomScaleFixed = false;
6409 if (updateZoomOverview) {
6410 WebSettings settings = getSettings();
6411 mInZoomOverview = settings.getUseWideViewPort() &&
6412 settings.getLoadWithOverviewMode();
6413 }
6414 } else {
6415 mMinZoomScale = restoreState.mDefaultScale;
6416 mMinZoomScaleFixed = true;
6417 }
6418 } else {
6419 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
6420 mMinZoomScaleFixed = false;
6421 }
6422 } else {
6423 mMinZoomScale = restoreState.mMinScale;
6424 mMinZoomScaleFixed = true;
6425 }
6426 if (restoreState.mMaxScale == 0) {
6427 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
6428 } else {
6429 mMaxZoomScale = restoreState.mMaxScale;
6430 }
6431 }
6432
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006433 /*
6434 * Request a dropdown menu for a listbox with single selection or a single
6435 * <select> element.
6436 *
6437 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006438 * @param enabledArray State for each element in the list. See static
6439 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006440 * @param selection Which position is initally selected.
6441 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006442 void requestListBox(String[] array, int[] enabledArray, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006443 mPrivateHandler.post(
6444 new InvokeListBox(array, enabledArray, selection));
6445 }
6446
6447 // called by JNI
Leon Scroggins47fabbf2009-12-08 16:57:26 -05006448 private void sendMoveFocus(int frame, int node) {
6449 mWebViewCore.sendMessage(EventHub.SET_MOVE_FOCUS,
6450 new WebViewCore.CursorData(frame, node, 0, 0));
6451 }
6452
6453 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04006454 private void sendMoveMouse(int frame, int node, int x, int y) {
6455 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
6456 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006457 }
6458
Leon Scroggins0658e8f2009-06-26 14:09:09 -04006459 /*
6460 * Send a mouse move event to the webcore thread.
6461 *
6462 * @param removeFocus Pass true if the "mouse" cursor is now over a node
6463 * which wants key events, but it is not the focus. This
6464 * will make the visual appear as though nothing is in
6465 * focus. Remove the WebTextView, if present, and stop
6466 * drawing the blinking caret.
6467 * called by JNI
6468 */
6469 private void sendMoveMouseIfLatest(boolean removeFocus) {
6470 if (removeFocus) {
6471 clearTextEntry();
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04006472 setFocusControllerInactive();
Cary Clark19436562009-06-04 16:25:07 -04006473 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04006474 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
6475 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006476 }
6477
6478 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04006479 private void sendMotionUp(int touchGeneration,
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006480 int frame, int node, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006481 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
6482 touchUpData.mMoveGeneration = touchGeneration;
Cary Clarkd6982c92009-05-29 11:02:22 -04006483 touchUpData.mFrame = frame;
6484 touchUpData.mNode = node;
6485 touchUpData.mX = x;
6486 touchUpData.mY = y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006487 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
6488 }
6489
6490
6491 private int getScaledMaxXScroll() {
6492 int width;
6493 if (mHeightCanMeasure == false) {
6494 width = getViewWidth() / 4;
6495 } else {
6496 Rect visRect = new Rect();
6497 calcOurVisibleRect(visRect);
6498 width = visRect.width() / 2;
6499 }
6500 // FIXME the divisor should be retrieved from somewhere
Leon Scroggins0236e672009-09-02 21:12:08 -04006501 return viewToContentX(width);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006502 }
6503
6504 private int getScaledMaxYScroll() {
6505 int height;
6506 if (mHeightCanMeasure == false) {
6507 height = getViewHeight() / 4;
6508 } else {
6509 Rect visRect = new Rect();
6510 calcOurVisibleRect(visRect);
6511 height = visRect.height() / 2;
6512 }
6513 // FIXME the divisor should be retrieved from somewhere
6514 // the closest thing today is hard-coded into ScrollView.java
6515 // (from ScrollView.java, line 363) int maxJump = height/2;
Cary Clarkdf344372009-09-15 12:47:39 -04006516 return Math.round(height * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006517 }
6518
6519 /**
6520 * Called by JNI to invalidate view
6521 */
6522 private void viewInvalidate() {
6523 invalidate();
6524 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006525
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006526 // return true if the key was handled
Cary Clark215b72c2009-06-26 14:38:43 -04006527 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
6528 long time, boolean ignorePlugin) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006529 if (mNativeClass == 0) {
6530 return false;
6531 }
Derek Sollenberger718d69f2009-10-19 15:56:43 -04006532 if (ignorePlugin == false && nativeFocusIsPlugin()) {
Cary Clark215b72c2009-06-26 14:38:43 -04006533 KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
6534 , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
6535 | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
6536 | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
6537 , 0, 0, 0);
6538 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
6539 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
6540 return true;
6541 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006542 mLastCursorTime = time;
6543 mLastCursorBounds = nativeGetCursorRingBounds();
6544 boolean keyHandled
6545 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006546 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006547 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
6548 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006549 + " handled=" + keyHandled);
6550 }
6551 if (keyHandled == false || mHeightCanMeasure == false) {
6552 return keyHandled;
6553 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006554 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
6555 if (contentCursorRingBounds.isEmpty()) return keyHandled;
Mike Reede9e86b82009-09-15 11:26:53 -04006556 Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006557 Rect visRect = new Rect();
6558 calcOurVisibleRect(visRect);
6559 Rect outset = new Rect(visRect);
6560 int maxXScroll = visRect.width() / 2;
6561 int maxYScroll = visRect.height() / 2;
6562 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006563 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006564 return keyHandled;
6565 }
6566 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006567 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
6568 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006569 if (maxH > 0) {
6570 pinScrollBy(maxH, 0, true, 0);
6571 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006572 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
6573 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006574 if (maxH < 0) {
6575 pinScrollBy(maxH, 0, true, 0);
6576 }
6577 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006578 if (mLastCursorBounds.isEmpty()) return keyHandled;
6579 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
6580 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006581 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006582 if (DebugFlags.WEB_VIEW) {
6583 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
6584 + contentCursorRingBounds);
6585 }
6586 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006587 mUserScroll = true;
6588 return keyHandled;
6589 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006590
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006591 /**
6592 * Set the background color. It's white by default. Pass
6593 * zero to make the view transparent.
6594 * @param color the ARGB color described by Color.java
6595 */
6596 public void setBackgroundColor(int color) {
6597 mBackgroundColor = color;
6598 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
6599 }
6600
6601 public void debugDump() {
6602 nativeDebugDump();
6603 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
6604 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006605
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006606 /**
Mike Reedefe2c722009-10-14 09:42:02 -04006607 * Draw the HTML page into the specified canvas. This call ignores any
6608 * view-specific zoom, scroll offset, or other changes. It does not draw
6609 * any view-specific chrome, such as progress or URL bars.
6610 *
6611 * @hide only needs to be accessible to Browser and testing
6612 */
6613 public void drawPage(Canvas canvas) {
6614 mWebViewCore.drawContentPicture(canvas, 0, false, false);
6615 }
6616
6617 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006618 * Update our cache with updatedText.
6619 * @param updatedText The new text to put in our cache.
6620 */
6621 /* package */ void updateCachedTextfield(String updatedText) {
6622 // Also place our generation number so that when we look at the cache
6623 // we recognize that it is up to date.
6624 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
6625 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006626
Cary Clark1cb97ee2009-12-11 12:10:36 -05006627 private native int nativeCacheHitFramePointer();
6628 private native Rect nativeCacheHitNodeBounds();
6629 private native int nativeCacheHitNodePointer();
Leon Scroggins4890feb2009-07-02 10:37:10 -04006630 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006631 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04006632 private native int nativeCursorFramePointer();
6633 private native Rect nativeCursorNodeBounds();
Cary Clarkaffa5d22010-01-07 12:18:23 -05006634 private native int nativeCursorNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04006635 /* package */ native boolean nativeCursorMatchesFocus();
6636 private native boolean nativeCursorIntersects(Rect visibleRect);
6637 private native boolean nativeCursorIsAnchor();
6638 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04006639 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04006640 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006641 /**
6642 * Returns true if the native cursor node says it wants to handle key events
6643 * (ala plugins). This can only be called if mNativeClass is non-zero!
6644 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04006645 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006646 private native void nativeDebugDump();
6647 private native void nativeDestroy();
Cary Clarkd6982c92009-05-29 11:02:22 -04006648 private native void nativeDrawCursorRing(Canvas content);
Nicolas Roard38863332010-01-04 19:30:55 +00006649 private native void nativeDestroyLayer(int layer);
6650 private native int nativeEvaluateLayersAnimations(int layer);
6651 private native boolean nativeLayersHaveAnimations(int layer);
Nicolas Roard493bcdd2010-01-19 21:08:49 +00006652 private native void nativeUpdateLayers(int updates);
Nicolas Roard38863332010-01-04 19:30:55 +00006653 private native void nativeDrawLayers(int layer,
Nicolas Roardaf2af4e52010-01-11 13:20:16 +00006654 int scrollX, int scrollY,
6655 int width, int height,
Nicolas Roard38863332010-01-04 19:30:55 +00006656 float scale, Canvas canvas);
Cary Clarkd6982c92009-05-29 11:02:22 -04006657 private native void nativeDrawMatches(Canvas canvas);
Cary Clark09e383c2009-10-26 16:43:58 -04006658 private native void nativeDrawSelectionPointer(Canvas content,
6659 float scale, int x, int y, boolean extendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006660 private native void nativeDrawSelectionRegion(Canvas content);
Cary Clarkd6982c92009-05-29 11:02:22 -04006661 private native void nativeDumpDisplayTree(String urlOrNull);
6662 private native int nativeFindAll(String findLower, String findUpper);
6663 private native void nativeFindNext(boolean forward);
Leon Scroggins3a503392010-01-06 17:04:38 -05006664 /* package */ native int nativeFocusCandidateFramePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006665 private native boolean nativeFocusCandidateIsPassword();
6666 private native boolean nativeFocusCandidateIsRtlText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006667 private native boolean nativeFocusCandidateIsTextInput();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05006668 /* package */ native int nativeFocusCandidateMaxLength();
Leon Scroggins0ca70882009-06-26 17:45:29 -04006669 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006670 private native Rect nativeFocusCandidateNodeBounds();
Cary Clarkaffa5d22010-01-07 12:18:23 -05006671 private native int nativeFocusCandidatePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006672 private native String nativeFocusCandidateText();
6673 private native int nativeFocusCandidateTextSize();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05006674 /**
6675 * Returns an integer corresponding to WebView.cpp::type.
6676 * See WebTextView.setType()
6677 */
6678 private native int nativeFocusCandidateType();
Derek Sollenberger718d69f2009-10-19 15:56:43 -04006679 private native boolean nativeFocusIsPlugin();
Leon Scroggins4890feb2009-07-02 10:37:10 -04006680 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04006681 private native Rect nativeGetCursorRingBounds();
Cary Clark57d2c3a2009-12-23 13:57:15 -05006682 private native String nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04006683 private native boolean nativeHasCursorNode();
6684 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04006685 private native void nativeHideCursor();
Cary Clarkd6982c92009-05-29 11:02:22 -04006686 private native String nativeImageURI(int x, int y);
6687 private native void nativeInstrumentReport();
Leon Scroggins01058282009-07-30 16:33:56 -04006688 /* package */ native void nativeMoveCursorToNextTextInput();
Cary Clarkd6982c92009-05-29 11:02:22 -04006689 // return true if the page has been scrolled
6690 private native boolean nativeMotionUp(int x, int y, int slop);
6691 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006692 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04006693 boolean noScroll);
6694 private native int nativeMoveGeneration();
6695 private native void nativeMoveSelection(int x, int y,
6696 boolean extendSelection);
Cary Clark1cb97ee2009-12-11 12:10:36 -05006697 private native boolean nativePointInNavCache(int x, int y, int slop);
Cary Clarkd6982c92009-05-29 11:02:22 -04006698 // Like many other of our native methods, you must make sure that
6699 // mNativeClass is not null before calling this method.
6700 private native void nativeRecordButtons(boolean focused,
6701 boolean pressed, boolean invalidate);
6702 private native void nativeSelectBestAt(Rect rect);
Cary Clark32847a92009-11-17 16:04:18 -05006703 private native void nativeSetFindIsUp();
Cary Clarkd6982c92009-05-29 11:02:22 -04006704 private native void nativeSetFollowedLink(boolean followed);
6705 private native void nativeSetHeightCanMeasure(boolean measure);
Leon Scroggins01058282009-07-30 16:33:56 -04006706 // Returns a value corresponding to CachedFrame::ImeAction
6707 /* package */ native int nativeTextFieldAction();
Cary Clarkd6982c92009-05-29 11:02:22 -04006708 private native int nativeTextGeneration();
6709 // Never call this version except by updateCachedTextfield(String) -
6710 // we always want to pass in our generation number.
6711 private native void nativeUpdateCachedTextfield(String updatedText,
6712 int generation);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006713 // return NO_LEFTEDGE means failure.
6714 private static final int NO_LEFTEDGE = -1;
Cary Clark77d98f42009-07-31 09:40:38 -04006715 private native int nativeGetBlockLeftEdge(int x, int y, float scale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006716}