blob: b5eb5d515cfa08bc4f0df7eabb4982a1347cb636 [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
Raphael30df2372010-03-06 10:09:54 -080019import android.annotation.Widget;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.app.AlertDialog;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.Intent;
24import android.content.DialogInterface.OnCancelListener;
Grace Kloba3a0def22010-01-23 21:11:54 -080025import android.content.pm.PackageManager;
Leon Scroggins3667ce42009-05-13 15:58:03 -040026import android.database.DataSetObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.graphics.Bitmap;
Grace Klobad7625dd2010-03-04 11:46:12 -080028import android.graphics.BitmapFactory;
29import android.graphics.BitmapShader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.graphics.Canvas;
31import android.graphics.Color;
Mike Reedc95326092010-02-08 16:49:58 -050032import android.graphics.Interpolator;
Grace Klobad7625dd2010-03-04 11:46:12 -080033import android.graphics.Paint;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.graphics.Picture;
35import android.graphics.Point;
36import android.graphics.Rect;
Grace Klobad7625dd2010-03-04 11:46:12 -080037import android.graphics.Region;
38import android.graphics.Shader;
Mike Reede8853fc2009-09-04 14:01:48 -040039import android.graphics.drawable.Drawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.net.http.SslCertificate;
41import android.net.Uri;
42import android.os.Bundle;
43import android.os.Handler;
44import android.os.Message;
45import android.os.ServiceManager;
46import android.os.SystemClock;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.text.IClipboard;
48import android.text.Selection;
49import android.text.Spannable;
50import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.util.EventLog;
52import android.util.Log;
Leon Scroggins3a6c88c2009-09-09 14:50:23 -040053import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.view.Gravity;
55import android.view.KeyEvent;
56import android.view.LayoutInflater;
57import android.view.MotionEvent;
Grace Kloba3a0def22010-01-23 21:11:54 -080058import android.view.ScaleGestureDetector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.view.SoundEffectConstants;
60import android.view.VelocityTracker;
61import android.view.View;
62import android.view.ViewConfiguration;
63import android.view.ViewGroup;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.view.ViewTreeObserver;
The Android Open Source Project10592532009-03-18 17:39:46 -070065import android.view.animation.AlphaAnimation;
Derek Sollenberger7cabb032010-01-21 10:37:38 -050066import android.view.inputmethod.EditorInfo;
67import android.view.inputmethod.InputConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.view.inputmethod.InputMethodManager;
Leon Scrogginsd3465f62009-06-02 10:57:54 -040069import android.webkit.WebTextView.AutoCompleteAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.webkit.WebViewCore.EventHub;
Grace Klobac2242f22010-03-05 14:00:26 -080071import android.webkit.WebViewCore.TouchEventData;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import android.widget.AbsoluteLayout;
Leon Scroggins3667ce42009-05-13 15:58:03 -040073import android.widget.Adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import android.widget.AdapterView;
75import android.widget.ArrayAdapter;
Leon Scrogginsa8da1732009-10-19 19:04:30 -040076import android.widget.CheckedTextView;
The Android Open Source Project10592532009-03-18 17:39:46 -070077import android.widget.FrameLayout;
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040078import android.widget.LinearLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079import android.widget.ListView;
Grace Klobad7625dd2010-03-04 11:46:12 -080080import android.widget.OverScroller;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081import android.widget.Toast;
82import android.widget.ZoomButtonsController;
The Android Open Source Project10592532009-03-18 17:39:46 -070083import android.widget.ZoomControls;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import android.widget.AdapterView.OnItemClickListener;
85
86import java.io.File;
87import java.io.FileInputStream;
88import java.io.FileNotFoundException;
89import java.io.FileOutputStream;
90import java.io.IOException;
91import java.net.URLDecoder;
92import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093import java.util.List;
Andrei Popescuf5dba882010-01-12 22:42:41 +000094import java.util.HashMap;
Andrei Popescu4950b2b2009-09-03 13:56:07 +010095import java.util.Map;
Andrei Popescua6d747d2010-02-11 13:19:21 +000096import java.util.Set;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040098import junit.framework.Assert;
99
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100/**
Cary Clarkd6982c92009-05-29 11:02:22 -0400101 * <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 -0800102 * can roll your own web browser or simply display some online content within your Activity.
103 * It uses the WebKit rendering engine to display
104 * web pages and includes methods to navigate forward and backward
105 * through a history, zoom in and out, perform text searches and more.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -0700106 * <p>To enable the built-in zoom, set
107 * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
108 * (introduced in API version 3).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109 * <p>Note that, in order for your Activity to access the Internet and load web pages
Cary Clarkd6982c92009-05-29 11:02:22 -0400110 * in a WebView, you must add the <var>INTERNET</var> permissions to your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111 * Android Manifest file:</p>
112 * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100113 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800114 * <p>This must be a child of the <code>&lt;manifest></code> element.</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100115 *
116 * <h3>Basic usage</h3>
117 *
118 * <p>By default, a WebView provides no browser-like widgets, does not
119 * enable JavaScript and errors will be ignored. If your goal is only
120 * to display some HTML as a part of your UI, this is probably fine;
121 * the user won't need to interact with the web page beyond reading
122 * it, and the web page won't need to interact with the user. If you
123 * actually want a fully blown web browser, then you probably want to
124 * invoke the Browser application with your URL rather than show it
125 * with a WebView. See {@link android.content.Intent} for more information.</p>
126 *
127 * <pre class="prettyprint">
128 * WebView webview = new WebView(this);
129 * setContentView(webview);
130 *
131 * // Simplest usage: note that an exception will NOT be thrown
132 * // if there is an error loading this page (see below).
133 * webview.loadUrl("http://slashdot.org/");
134 *
135 * // Of course you can also load from any string:
136 * String summary = "&lt;html>&lt;body>You scored &lt;b>192</b> points.&lt;/body>&lt;/html>";
137 * webview.loadData(summary, "text/html", "utf-8");
138 * // ... although note that there are restrictions on what this HTML can do.
139 * // See the JavaDocs for loadData and loadDataWithBaseUrl for more info.
140 * </pre>
141 *
142 * <p>A WebView has several customization points where you can add your
143 * own behavior. These are:</p>
144 *
145 * <ul>
146 * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
147 * This class is called when something that might impact a
148 * browser UI happens, for instance, progress updates and
149 * JavaScript alerts are sent here.
150 * </li>
151 * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
152 * It will be called when things happen that impact the
153 * rendering of the content, eg, errors or form submissions. You
154 * can also intercept URL loading here.</li>
155 * <li>Via the {@link android.webkit.WebSettings} class, which contains
156 * miscellaneous configuration. </li>
157 * <li>With the {@link android.webkit.WebView#addJavascriptInterface} method.
158 * This lets you bind Java objects into the WebView so they can be
159 * controlled from the web pages JavaScript.</li>
160 * </ul>
161 *
162 * <p>Here's a more complicated example, showing error handling,
163 * settings, and progress notification:</p>
164 *
165 * <pre class="prettyprint">
166 * // Let's display the progress in the activity title bar, like the
167 * // browser app does.
168 * getWindow().requestFeature(Window.FEATURE_PROGRESS);
169 *
170 * webview.getSettings().setJavaScriptEnabled(true);
171 *
172 * final Activity activity = this;
173 * webview.setWebChromeClient(new WebChromeClient() {
174 * public void onProgressChanged(WebView view, int progress) {
175 * // Activities and WebViews measure progress with different scales.
176 * // The progress meter will automatically disappear when we reach 100%
177 * activity.setProgress(progress * 1000);
178 * }
179 * });
180 * webview.setWebViewClient(new WebViewClient() {
181 * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
182 * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
183 * }
184 * });
185 *
186 * webview.loadUrl("http://slashdot.org/");
187 * </pre>
188 *
189 * <h3>Cookie and window management</h3>
190 *
191 * <p>For obvious security reasons, your application has its own
192 * cache, cookie store etc - it does not share the Browser
193 * applications data. Cookies are managed on a separate thread, so
194 * operations like index building don't block the UI
195 * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
196 * if you want to use cookies in your application.
197 * </p>
198 *
199 * <p>By default, requests by the HTML to open new windows are
200 * ignored. This is true whether they be opened by JavaScript or by
201 * the target attribute on a link. You can customize your
202 * WebChromeClient to provide your own behaviour for opening multiple windows,
203 * and render them in whatever manner you want.</p>
204 *
205 * <p>Standard behavior for an Activity is to be destroyed and
206 * recreated when the devices orientation is changed. This will cause
207 * the WebView to reload the current page. If you don't want that, you
208 * can set your Activity to handle the orientation and keyboardHidden
209 * changes, and then just leave the WebView alone. It'll automatically
210 * re-orient itself as appropriate.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800211 */
Raphael30df2372010-03-06 10:09:54 -0800212@Widget
Cary Clarkd6982c92009-05-29 11:02:22 -0400213public class WebView extends AbsoluteLayout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800214 implements ViewTreeObserver.OnGlobalFocusChangeListener,
215 ViewGroup.OnHierarchyChangeListener {
216
Mike Reedfdc54242010-01-08 10:29:51 -0500217 // enable debug output for drag trackers
Mike Reedb2924732010-01-08 17:34:58 -0500218 private static final boolean DEBUG_DRAG_TRACKER = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800219 // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
220 // the screen all-the-time. Good for profiling our drawing code
221 static private final boolean AUTO_REDRAW_HACK = false;
222 // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
223 private boolean mAutoRedraw;
224
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800225 static final String LOGTAG = "webview";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226
Leon Scroggins213a31c2009-05-21 16:49:32 -0700227 private static class ExtendedZoomControls extends FrameLayout {
The Android Open Source Project10592532009-03-18 17:39:46 -0700228 public ExtendedZoomControls(Context context, AttributeSet attrs) {
229 super(context, attrs);
230 LayoutInflater inflater = (LayoutInflater)
231 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
232 inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400233 mPlusMinusZoomControls = (ZoomControls) findViewById(
234 com.android.internal.R.id.zoomControls);
Grace Kloba04b28682009-09-14 14:38:37 -0700235 findViewById(com.android.internal.R.id.zoomMagnify).setVisibility(
236 View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700237 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400238
The Android Open Source Project10592532009-03-18 17:39:46 -0700239 public void show(boolean showZoom, boolean canZoomOut) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400240 mPlusMinusZoomControls.setVisibility(
241 showZoom ? View.VISIBLE : View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700242 fade(View.VISIBLE, 0.0f, 1.0f);
243 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400244
The Android Open Source Project10592532009-03-18 17:39:46 -0700245 public void hide() {
246 fade(View.GONE, 1.0f, 0.0f);
247 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400248
The Android Open Source Project10592532009-03-18 17:39:46 -0700249 private void fade(int visibility, float startAlpha, float endAlpha) {
250 AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
251 anim.setDuration(500);
252 startAnimation(anim);
253 setVisibility(visibility);
254 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400255
The Android Open Source Project10592532009-03-18 17:39:46 -0700256 public boolean hasFocus() {
Grace Kloba04b28682009-09-14 14:38:37 -0700257 return mPlusMinusZoomControls.hasFocus();
The Android Open Source Project10592532009-03-18 17:39:46 -0700258 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400259
The Android Open Source Project10592532009-03-18 17:39:46 -0700260 public void setOnZoomInClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400261 mPlusMinusZoomControls.setOnZoomInClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700262 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400263
The Android Open Source Project10592532009-03-18 17:39:46 -0700264 public void setOnZoomOutClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400265 mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700266 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400267
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400268 ZoomControls mPlusMinusZoomControls;
The Android Open Source Project10592532009-03-18 17:39:46 -0700269 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400270
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271 /**
272 * Transportation object for returning WebView across thread boundaries.
273 */
274 public class WebViewTransport {
275 private WebView mWebview;
276
277 /**
278 * Set the WebView to the transportation object.
279 * @param webview The WebView to transport.
280 */
281 public synchronized void setWebView(WebView webview) {
282 mWebview = webview;
283 }
284
285 /**
286 * Return the WebView object.
287 * @return WebView The transported WebView object.
288 */
289 public synchronized WebView getWebView() {
290 return mWebview;
291 }
292 }
293
294 // A final CallbackProxy shared by WebViewCore and BrowserFrame.
295 private final CallbackProxy mCallbackProxy;
296
297 private final WebViewDatabase mDatabase;
298
299 // SSL certificate for the main top-level page (if secure)
300 private SslCertificate mCertificate;
301
302 // Native WebView pointer that is 0 until the native object has been
303 // created.
304 private int mNativeClass;
305 // This would be final but it needs to be set to null when the WebView is
306 // destroyed.
307 private WebViewCore mWebViewCore;
308 // Handler for dispatching UI messages.
309 /* package */ final Handler mPrivateHandler = new PrivateHandler();
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400310 private WebTextView mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 // Used to ignore changes to webkit text that arrives to the UI side after
312 // more key events.
313 private int mTextGeneration;
314
Patrick Scott0a5ce012009-07-02 08:56:10 -0400315 // Used by WebViewCore to create child views.
316 /* package */ final ViewManager mViewManager;
317
Grace Kloba11438c32009-12-16 11:39:12 -0800318 // Used to display in full screen mode
319 PluginFullScreenHolder mFullScreenHolder;
320
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800321 /**
322 * Position of the last touch event.
323 */
324 private float mLastTouchX;
325 private float mLastTouchY;
326
327 /**
328 * Time of the last touch event.
329 */
330 private long mLastTouchTime;
331
332 /**
333 * Time of the last time sending touch event to WebViewCore
334 */
335 private long mLastSentTouchTime;
336
337 /**
338 * The minimum elapsed time before sending another ACTION_MOVE event to
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700339 * WebViewCore. This really should be tuned for each type of the devices.
340 * For example in Google Map api test case, it takes Dream device at least
341 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
342 * triggering the layout and drawing the picture. While the same process
343 * takes 60+ms on the current high speed device. If we make
344 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
345 * to WebViewCore queue and the real layout and draw events will be pushed
346 * to further, which slows down the refresh rate. Choose 50 to favor the
347 * current high speed devices. For Dream like devices, 100 is a better
348 * choice. Maybe make this in the buildspec later.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800349 */
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700350 private static final int TOUCH_SENT_INTERVAL = 50;
Ben Murdochecbc65c2010-01-13 10:54:56 +0000351 private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352
353 /**
354 * Helper class to get velocity for fling
355 */
356 VelocityTracker mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700357 private int mMaximumFling;
Cary Clark278ce052009-08-31 16:08:42 -0400358 private float mLastVelocity;
359 private float mLastVelX;
360 private float mLastVelY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 /**
363 * Touch mode
364 */
365 private int mTouchMode = TOUCH_DONE_MODE;
366 private static final int TOUCH_INIT_MODE = 1;
367 private static final int TOUCH_DRAG_START_MODE = 2;
368 private static final int TOUCH_DRAG_MODE = 3;
369 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
370 private static final int TOUCH_SHORTPRESS_MODE = 5;
Grace Kloba04b28682009-09-14 14:38:37 -0700371 private static final int TOUCH_DOUBLE_TAP_MODE = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800372 private static final int TOUCH_DONE_MODE = 7;
373 private static final int TOUCH_SELECT_MODE = 8;
Grace Kloba3a0def22010-01-23 21:11:54 -0800374 private static final int TOUCH_PINCH_DRAG = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800375
376 // Whether to forward the touch events to WebCore
377 private boolean mForwardTouchEvents = false;
378
Grace Klobac2242f22010-03-05 14:00:26 -0800379 // Whether to prevent default during touch. The initial value depends on
380 // mForwardTouchEvents. If WebCore wants all the touch events, it says yes
381 // for touch down. Otherwise UI will wait for the answer of the first
382 // confirmed move before taking over the control.
383 private static final int PREVENT_DEFAULT_NO = 0;
384 private static final int PREVENT_DEFAULT_MAYBE_YES = 1;
385 private static final int PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN = 2;
386 private static final int PREVENT_DEFAULT_YES = 3;
387 private static final int PREVENT_DEFAULT_IGNORE = 4;
388 private int mPreventDefault = PREVENT_DEFAULT_IGNORE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800389
Grace Klobac2242f22010-03-05 14:00:26 -0800390 // true when the touch movement exceeds the slop
391 private boolean mConfirmMove;
Grace Kloba5f68d6f2009-12-08 18:42:54 -0800392
Grace Klobac2242f22010-03-05 14:00:26 -0800393 // if true, touch events will be first processed by WebCore, if prevent
394 // default is not set, the UI will continue handle them.
395 private boolean mDeferTouchProcess;
396
397 // to avoid interfering with the current touch events, track them
398 // separately. Currently no snapping or fling in the deferred process mode
399 private int mDeferTouchMode = TOUCH_DONE_MODE;
400 private float mLastDeferTouchX;
401 private float mLastDeferTouchY;
Grace Kloba5f68d6f2009-12-08 18:42:54 -0800402
Leon Scroggins72543e12009-07-23 15:29:45 -0400403 // To keep track of whether the current drag was initiated by a WebTextView,
404 // so that we know not to hide the cursor
405 boolean mDragFromTextInput;
406
Cary Clarkd6982c92009-05-29 11:02:22 -0400407 // Whether or not to draw the cursor ring.
408 private boolean mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409
Mike Reedd205d5b2009-05-27 11:02:29 -0400410 // true if onPause has been called (and not onResume)
411 private boolean mIsPaused;
412
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800413 /**
414 * Customizable constant
415 */
416 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
417 private int mTouchSlopSquare;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700418 // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
419 private int mDoubleTapSlopSquare;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700420 // pre-computed density adjusted navigation slop
421 private int mNavSlop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 // This should be ViewConfiguration.getTapTimeout()
423 // But system time out is 100ms, which is too short for the browser.
424 // In the browser, if it switches out of tap too soon, jump tap won't work.
425 private static final int TAP_TIMEOUT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800426 // This should be ViewConfiguration.getLongPressTimeout()
427 // But system time out is 500ms, which is too short for the browser.
428 // With a short timeout, it's difficult to treat trigger a short press.
429 private static final int LONG_PRESS_TIMEOUT = 1000;
430 // needed to avoid flinging after a pause of no movement
431 private static final int MIN_FLING_TIME = 250;
Cary Clark25415e22009-10-12 13:41:28 -0400432 // draw unfiltered after drag is held without movement
433 private static final int MOTIONLESS_TIME = 100;
The Android Open Source Project10592532009-03-18 17:39:46 -0700434 // The time that the Zoom Controls are visible before fading away
Cary Clarkd6982c92009-05-29 11:02:22 -0400435 private static final long ZOOM_CONTROLS_TIMEOUT =
The Android Open Source Project10592532009-03-18 17:39:46 -0700436 ViewConfiguration.getZoomControlsTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 // The amount of content to overlap between two screens when going through
438 // pages with the space bar, in pixels.
439 private static final int PAGE_SCROLL_OVERLAP = 24;
440
441 /**
442 * These prevent calling requestLayout if either dimension is fixed. This
443 * depends on the layout parameters and the measure specs.
444 */
445 boolean mWidthCanMeasure;
446 boolean mHeightCanMeasure;
447
448 // Remember the last dimensions we sent to the native side so we can avoid
449 // sending the same dimensions more than once.
450 int mLastWidthSent;
451 int mLastHeightSent;
452
453 private int mContentWidth; // cache of value from WebViewCore
454 private int mContentHeight; // cache of value from WebViewCore
455
Cary Clarkd6982c92009-05-29 11:02:22 -0400456 // Need to have the separate control for horizontal and vertical scrollbar
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800457 // style than the View's single scrollbar style
458 private boolean mOverlayHorizontalScrollbar = true;
459 private boolean mOverlayVerticalScrollbar = false;
460
461 // our standard speed. this way small distances will be traversed in less
462 // time than large distances, but we cap the duration, so that very large
463 // distances won't take too long to get there.
464 private static final int STD_SPEED = 480; // pixels per second
465 // time for the longest scroll animation
466 private static final int MAX_DURATION = 750; // milliseconds
Leon Scroggins03c87bf2009-09-18 15:05:59 -0400467 private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
Grace Klobad7625dd2010-03-04 11:46:12 -0800468 private OverScroller mScroller;
469 private boolean mInOverScrollMode = false;
470 private static Paint mOverScrollBackground;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800471
472 private boolean mWrapContent;
Cary Clark25415e22009-10-12 13:41:28 -0400473 private static final int MOTIONLESS_FALSE = 0;
474 private static final int MOTIONLESS_PENDING = 1;
475 private static final int MOTIONLESS_TRUE = 2;
476 private int mHeldMotionless;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477
Grace Kloba3a0def22010-01-23 21:11:54 -0800478 // whether support multi-touch
Grace Klobaf3be1662010-02-02 11:17:55 -0800479 private boolean mSupportMultiTouch;
Grace Kloba3a0def22010-01-23 21:11:54 -0800480 // use the framework's ScaleGestureDetector to handle multi-touch
481 private ScaleGestureDetector mScaleDetector;
Grace Kloba3a0def22010-01-23 21:11:54 -0800482
483 // the anchor point in the document space where VIEW_SIZE_CHANGED should
484 // apply to
485 private int mAnchorX;
486 private int mAnchorY;
487
Grace Klobac2242f22010-03-05 14:00:26 -0800488 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800489 * Private message ids
490 */
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400491 private static final int REMEMBER_PASSWORD = 1;
492 private static final int NEVER_REMEMBER_PASSWORD = 2;
493 private static final int SWITCH_TO_SHORTPRESS = 3;
494 private static final int SWITCH_TO_LONGPRESS = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700495 private static final int RELEASE_SINGLE_TAP = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400496 private static final int REQUEST_FORM_DATA = 6;
Grace Kloba96949ef2010-01-25 09:53:01 -0800497 private static final int RESUME_WEBCORE_PRIORITY = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400498 private static final int DRAG_HELD_MOTIONLESS = 8;
499 private static final int AWAKEN_SCROLL_BARS = 9;
Grace Klobac2242f22010-03-05 14:00:26 -0800500 private static final int PREVENT_DEFAULT_TIMEOUT = 10;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800501
Grace Klobac2242f22010-03-05 14:00:26 -0800502 private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD;
503 private static final int LAST_PRIVATE_MSG_ID = PREVENT_DEFAULT_TIMEOUT;
504
505 /*
506 * Package message ids
507 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508 //! arg1=x, arg2=y
Grace Klobac2242f22010-03-05 14:00:26 -0800509 static final int SCROLL_TO_MSG_ID = 101;
510 static final int SCROLL_BY_MSG_ID = 102;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 //! arg1=x, arg2=y
Grace Klobac2242f22010-03-05 14:00:26 -0800512 static final int SPAWN_SCROLL_TO_MSG_ID = 103;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513 //! arg1=x, arg2=y
Grace Klobac2242f22010-03-05 14:00:26 -0800514 static final int SYNC_SCROLL_TO_MSG_ID = 104;
515 static final int NEW_PICTURE_MSG_ID = 105;
516 static final int UPDATE_TEXT_ENTRY_MSG_ID = 106;
517 static final int WEBCORE_INITIALIZED_MSG_ID = 107;
518 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 108;
519 static final int UPDATE_ZOOM_RANGE = 109;
520 static final int MOVE_OUT_OF_PLUGIN = 110;
521 static final int CLEAR_TEXT_ENTRY = 111;
522 static final int UPDATE_TEXT_SELECTION_MSG_ID = 112;
523 static final int SHOW_RECT_MSG_ID = 113;
524 static final int LONG_PRESS_CENTER = 114;
525 static final int PREVENT_TOUCH_ID = 115;
526 static final int WEBCORE_NEED_TOUCH_EVENTS = 116;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800527 // obj=Rect in doc coordinates
Grace Klobac2242f22010-03-05 14:00:26 -0800528 static final int INVAL_RECT_MSG_ID = 117;
529 static final int REQUEST_KEYBOARD = 118;
530 static final int DO_MOTION_UP = 119;
531 static final int SHOW_FULLSCREEN = 120;
532 static final int HIDE_FULLSCREEN = 121;
533 static final int DOM_FOCUS_CHANGED = 122;
534 static final int IMMEDIATE_REPAINT_MSG_ID = 123;
535 static final int SET_ROOT_LAYER_MSG_ID = 124;
536 static final int RETURN_LABEL = 125;
537 static final int FIND_AGAIN = 126;
Grace Klobae8300a12010-03-12 13:32:55 -0800538 static final int CENTER_FIT_RECT = 127;
Leon Scrogginsb4157792010-03-18 12:42:33 -0400539 static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
Cary Clarkd6982c92009-05-29 11:02:22 -0400540
Grace Klobac2242f22010-03-05 14:00:26 -0800541 private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
Leon Scrogginsb4157792010-03-18 12:42:33 -0400542 private static final int LAST_PACKAGE_MSG_ID
543 = REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID;
Grace Klobac2242f22010-03-05 14:00:26 -0800544
545 static final String[] HandlerPrivateDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400546 "REMEMBER_PASSWORD", // = 1;
547 "NEVER_REMEMBER_PASSWORD", // = 2;
548 "SWITCH_TO_SHORTPRESS", // = 3;
549 "SWITCH_TO_LONGPRESS", // = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700550 "RELEASE_SINGLE_TAP", // = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400551 "REQUEST_FORM_DATA", // = 6;
Grace Kloba96949ef2010-01-25 09:53:01 -0800552 "RESUME_WEBCORE_PRIORITY", // = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400553 "DRAG_HELD_MOTIONLESS", // = 8;
554 "AWAKEN_SCROLL_BARS", // = 9;
Grace Klobac2242f22010-03-05 14:00:26 -0800555 "PREVENT_DEFAULT_TIMEOUT" // = 10;
556 };
557
558 static final String[] HandlerPackageDebugString = {
559 "SCROLL_TO_MSG_ID", // = 101;
560 "SCROLL_BY_MSG_ID", // = 102;
561 "SPAWN_SCROLL_TO_MSG_ID", // = 103;
562 "SYNC_SCROLL_TO_MSG_ID", // = 104;
563 "NEW_PICTURE_MSG_ID", // = 105;
564 "UPDATE_TEXT_ENTRY_MSG_ID", // = 106;
565 "WEBCORE_INITIALIZED_MSG_ID", // = 107;
566 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 108;
567 "UPDATE_ZOOM_RANGE", // = 109;
568 "MOVE_OUT_OF_PLUGIN", // = 110;
569 "CLEAR_TEXT_ENTRY", // = 111;
570 "UPDATE_TEXT_SELECTION_MSG_ID", // = 112;
571 "SHOW_RECT_MSG_ID", // = 113;
572 "LONG_PRESS_CENTER", // = 114;
573 "PREVENT_TOUCH_ID", // = 115;
574 "WEBCORE_NEED_TOUCH_EVENTS", // = 116;
575 "INVAL_RECT_MSG_ID", // = 117;
576 "REQUEST_KEYBOARD", // = 118;
577 "DO_MOTION_UP", // = 119;
578 "SHOW_FULLSCREEN", // = 120;
579 "HIDE_FULLSCREEN", // = 121;
580 "DOM_FOCUS_CHANGED", // = 122;
581 "IMMEDIATE_REPAINT_MSG_ID", // = 123;
582 "SET_ROOT_LAYER_MSG_ID", // = 124;
583 "RETURN_LABEL", // = 125;
Grace Klobae8300a12010-03-12 13:32:55 -0800584 "FIND_AGAIN", // = 126;
Leon Scrogginsb4157792010-03-18 12:42:33 -0400585 "CENTER_FIT_RECT", // = 127;
586 "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID" // = 128;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 };
588
Grace Klobaa4fa1072009-11-09 12:01:50 -0800589 // If the site doesn't use the viewport meta tag to specify the viewport,
590 // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
591 static final int DEFAULT_VIEWPORT_WIDTH = 800;
592
593 // normally we try to fit the content to the minimum preferred width
594 // calculated by the Webkit. To avoid the bad behavior when some site's
595 // minimum preferred width keeps growing when changing the viewport width or
596 // the minimum preferred width is huge, an upper limit is needed.
597 static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
598
Grace Kloba25737912009-06-19 12:42:47 -0700599 // default scale limit. Depending on the display density
600 private static float DEFAULT_MAX_ZOOM_SCALE;
601 private static float DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 // scale limit, which can be set through viewport meta tag in the web page
Grace Kloba25737912009-06-19 12:42:47 -0700603 private float mMaxZoomScale;
604 private float mMinZoomScale;
Grace Klobae397a882009-08-06 12:04:14 -0700605 private boolean mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800606
607 // initial scale in percent. 0 means using default.
Grace Kloba16efce72009-11-10 15:49:03 -0800608 private int mInitialScaleInPercent = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800609
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700610 // while in the zoom overview mode, the page's width is fully fit to the
611 // current window. The page is alive, in another words, you can click to
612 // follow the links. Double tap will toggle between zoom overview mode and
613 // the last zoom scale.
614 boolean mInZoomOverview = false;
Leon Scrogginsf58ffac2009-08-13 15:34:06 -0400615
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700616 // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
617 // engadget always have wider mContentWidth no matter what viewport size is.
Grace Klobaa4fa1072009-11-09 12:01:50 -0800618 int mZoomOverviewWidth = DEFAULT_VIEWPORT_WIDTH;
Grace Kloba3a0def22010-01-23 21:11:54 -0800619 float mTextWrapScale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700620
Grace Kloba25737912009-06-19 12:42:47 -0700621 // default scale. Depending on the display density.
622 static int DEFAULT_SCALE_PERCENT;
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700623 private float mDefaultScale;
Grace Kloba25737912009-06-19 12:42:47 -0700624
Grace Klobac6f95fe2010-03-10 13:25:34 -0800625 private static float MINIMUM_SCALE_INCREMENT = 0.01f;
626
Grace Kloba3a0def22010-01-23 21:11:54 -0800627 // set to true temporarily during ScaleGesture triggered zoom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 private boolean mPreviewZoomOnly = false;
629
630 // computed scale and inverse, from mZoomWidth.
Grace Kloba25737912009-06-19 12:42:47 -0700631 private float mActualScale;
632 private float mInvActualScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 // if this is non-zero, it is used on drawing rather than mActualScale
634 private float mZoomScale;
635 private float mInvInitialZoomScale;
636 private float mInvFinalZoomScale;
Grace Kloba675c7d22009-07-23 09:21:21 -0700637 private int mInitialScrollX;
638 private int mInitialScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 private long mZoomStart;
640 private static final int ZOOM_ANIMATION_LENGTH = 500;
641
642 private boolean mUserScroll = false;
643
644 private int mSnapScrollMode = SNAP_NONE;
Cary Clarkac492e12009-10-14 14:53:37 -0400645 private static final int SNAP_NONE = 0;
646 private static final int SNAP_LOCK = 1; // not a separate state
647 private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
648 private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400650
Cary Clark2ec30692010-02-23 10:50:38 -0500651 // keep these in sync with their counterparts in WebView.cpp
652 private static final int DRAW_EXTRAS_NONE = 0;
653 private static final int DRAW_EXTRAS_FIND = 1;
654 private static final int DRAW_EXTRAS_SELECTION = 2;
655 private static final int DRAW_EXTRAS_CURSOR_RING = 3;
656
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800657 // Used to match key downs and key ups
658 private boolean mGotKeyDown;
659
660 /* package */ static boolean mLogEvent = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800661
662 // for event log
663 private long mLastTouchUpTime = 0;
664
665 /**
666 * URI scheme for telephone number
667 */
668 public static final String SCHEME_TEL = "tel:";
669 /**
670 * URI scheme for email address
671 */
672 public static final String SCHEME_MAILTO = "mailto:";
673 /**
674 * URI scheme for map address
675 */
676 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400677
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800678 private int mBackgroundColor = Color.WHITE;
679
680 // Used to notify listeners of a new picture.
681 private PictureListener mPictureListener;
682 /**
683 * Interface to listen for new pictures as they change.
684 */
685 public interface PictureListener {
686 /**
687 * Notify the listener that the picture has changed.
688 * @param view The WebView that owns the picture.
689 * @param picture The new picture.
690 */
691 public void onNewPicture(WebView view, Picture picture);
692 }
693
Leon Scroggins3246c222009-05-26 09:51:23 -0400694 // FIXME: Want to make this public, but need to change the API file.
695 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 /**
697 * Default HitTestResult, where the target is unknown
698 */
699 public static final int UNKNOWN_TYPE = 0;
700 /**
701 * HitTestResult for hitting a HTML::a tag
702 */
703 public static final int ANCHOR_TYPE = 1;
704 /**
705 * HitTestResult for hitting a phone number
706 */
707 public static final int PHONE_TYPE = 2;
708 /**
709 * HitTestResult for hitting a map address
710 */
711 public static final int GEO_TYPE = 3;
712 /**
713 * HitTestResult for hitting an email address
714 */
715 public static final int EMAIL_TYPE = 4;
716 /**
717 * HitTestResult for hitting an HTML::img tag
718 */
719 public static final int IMAGE_TYPE = 5;
720 /**
721 * HitTestResult for hitting a HTML::a tag which contains HTML::img
722 */
723 public static final int IMAGE_ANCHOR_TYPE = 6;
724 /**
725 * HitTestResult for hitting a HTML::a tag with src=http
726 */
727 public static final int SRC_ANCHOR_TYPE = 7;
728 /**
729 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
730 */
731 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
732 /**
733 * HitTestResult for hitting an edit text area
734 */
735 public static final int EDIT_TEXT_TYPE = 9;
736
737 private int mType;
738 private String mExtra;
739
740 HitTestResult() {
741 mType = UNKNOWN_TYPE;
742 }
743
744 private void setType(int type) {
745 mType = type;
746 }
747
748 private void setExtra(String extra) {
749 mExtra = extra;
750 }
751
752 public int getType() {
753 return mType;
754 }
755
756 public String getExtra() {
757 return mExtra;
758 }
759 }
760
The Android Open Source Project10592532009-03-18 17:39:46 -0700761 // The View containing the zoom controls
762 private ExtendedZoomControls mZoomControls;
763 private Runnable mZoomControlRunnable;
764
Cary Clarkd6982c92009-05-29 11:02:22 -0400765 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800766
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700767 // These keep track of the center point of the zoom. They are used to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800768 // determine the point around which we should zoom.
769 private float mZoomCenterX;
770 private float mZoomCenterY;
771
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700772 private ZoomButtonsController.OnZoomListener mZoomListener =
773 new ZoomButtonsController.OnZoomListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 public void onVisibilityChanged(boolean visible) {
776 if (visible) {
777 switchOutDrawHistory();
Mike Reed8b302092009-11-12 12:50:20 -0500778 // Bring back the hidden zoom controls.
779 mZoomButtonsController.getZoomControls().setVisibility(
780 View.VISIBLE);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700781 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800782 }
783 }
784
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700785 public void onZoom(boolean zoomIn) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800786 if (zoomIn) {
787 zoomIn();
788 } else {
789 zoomOut();
790 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400791
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700792 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 };
Cary Clarkd6982c92009-05-29 11:02:22 -0400795
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 /**
797 * Construct a new WebView with a Context object.
798 * @param context A Context object used to access application assets.
799 */
800 public WebView(Context context) {
801 this(context, null);
802 }
803
804 /**
805 * Construct a new WebView with layout parameters.
806 * @param context A Context object used to access application assets.
807 * @param attrs An AttributeSet passed to our parent.
808 */
809 public WebView(Context context, AttributeSet attrs) {
810 this(context, attrs, com.android.internal.R.attr.webViewStyle);
811 }
812
813 /**
814 * Construct a new WebView with layout parameters and a default style.
815 * @param context A Context object used to access application assets.
816 * @param attrs An AttributeSet passed to our parent.
817 * @param defStyle The default style resource ID.
818 */
819 public WebView(Context context, AttributeSet attrs, int defStyle) {
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100820 this(context, attrs, defStyle, null);
821 }
822
823 /**
824 * Construct a new WebView with layout parameters, a default style and a set
825 * of custom Javscript interfaces to be added to the WebView at initialization
Romain Guy01d0fbf2009-12-01 14:52:19 -0800826 * time. This guarantees that these interfaces will be available when the JS
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100827 * context is initialized.
828 * @param context A Context object used to access application assets.
829 * @param attrs An AttributeSet passed to our parent.
830 * @param defStyle The default style resource ID.
831 * @param javascriptInterfaces is a Map of intareface names, as keys, and
832 * object implementing those interfaces, as values.
833 * @hide pending API council approval.
834 */
835 protected WebView(Context context, AttributeSet attrs, int defStyle,
836 Map<String, Object> javascriptInterfaces) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 super(context, attrs, defStyle);
838 init();
839
840 mCallbackProxy = new CallbackProxy(context, this);
Grace Kloba9a67c822009-12-20 11:33:58 -0800841 mViewManager = new ViewManager(this);
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100842 mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 mDatabase = WebViewDatabase.getInstance(context);
Grace Klobad7625dd2010-03-04 11:46:12 -0800844 mScroller = new OverScroller(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700845
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700846 mZoomButtonsController = new ZoomButtonsController(this);
847 mZoomButtonsController.setOnZoomListener(mZoomListener);
Leon Scrogginsaa3f96a2009-06-11 14:46:35 -0400848 // ZoomButtonsController positions the buttons at the bottom, but in
849 // the middle. Change their layout parameters so they appear on the
850 // right.
851 View controls = mZoomButtonsController.getZoomControls();
852 ViewGroup.LayoutParams params = controls.getLayoutParams();
853 if (params instanceof FrameLayout.LayoutParams) {
854 FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
855 params;
856 frameParams.gravity = Gravity.RIGHT;
857 }
Grace Kloba3a0def22010-01-23 21:11:54 -0800858 updateMultiTouchSupport(context);
859 }
860
861 void updateMultiTouchSupport(Context context) {
862 WebSettings settings = getSettings();
863 mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
864 PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
865 && settings.supportZoom() && settings.getBuiltInZoomControls();
866 if (mSupportMultiTouch && (mScaleDetector == null)) {
867 mScaleDetector = new ScaleGestureDetector(context,
868 new ScaleDetectorListener());
869 } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
870 mScaleDetector = null;
871 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700872 }
873
874 private void updateZoomButtonsEnabled() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700875 boolean canZoomIn = mActualScale < mMaxZoomScale;
Grace Kloba455e3af2009-08-13 11:01:21 -0700876 boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview;
The Android Open Source Project10592532009-03-18 17:39:46 -0700877 if (!canZoomIn && !canZoomOut) {
878 // Hide the zoom in and out buttons, as well as the fit to page
879 // button, if the page cannot zoom
880 mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700881 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -0700882 // Set each one individually, as a page may be able to zoom in
883 // or out.
884 mZoomButtonsController.setZoomInEnabled(canZoomIn);
885 mZoomButtonsController.setZoomOutEnabled(canZoomOut);
The Android Open Source Project10592532009-03-18 17:39:46 -0700886 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800887 }
888
889 private void init() {
890 setWillNotDraw(false);
891 setFocusable(true);
892 setFocusableInTouchMode(true);
893 setClickable(true);
894 setLongClickable(true);
895
Romain Guy4296fc42009-07-06 11:48:52 -0700896 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700897 int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 mTouchSlopSquare = slop * slop;
899 mMinLockSnapReverseDistance = slop;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700900 slop = configuration.getScaledDoubleTapSlop();
901 mDoubleTapSlopSquare = slop * slop;
Grace Kloba25737912009-06-19 12:42:47 -0700902 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700903 // use one line height, 16 based on our current default font, for how
904 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -0700905 mNavSlop = (int) (16 * density);
906 // density adjusted scale factors
907 DEFAULT_SCALE_PERCENT = (int) (100 * density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700908 mDefaultScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700909 mActualScale = density;
910 mInvActualScale = 1 / density;
Grace Kloba3a0def22010-01-23 21:11:54 -0800911 mTextWrapScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700912 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
913 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
914 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
915 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
Romain Guy4296fc42009-07-06 11:48:52 -0700916 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 }
918
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700919 /* package */void updateDefaultZoomDensity(int zoomDensity) {
920 final float density = getContext().getResources().getDisplayMetrics().density
921 * 100 / zoomDensity;
922 if (Math.abs(density - mDefaultScale) > 0.01) {
923 float scaleFactor = density / mDefaultScale;
924 // adjust the limits
925 mNavSlop = (int) (16 * density);
926 DEFAULT_SCALE_PERCENT = (int) (100 * density);
927 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
928 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
929 mDefaultScale = density;
930 mMaxZoomScale *= scaleFactor;
931 mMinZoomScale *= scaleFactor;
Grace Kloba3a0def22010-01-23 21:11:54 -0800932 setNewZoomScale(mActualScale * scaleFactor, true, false);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700933 }
934 }
935
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 /* package */ boolean onSavePassword(String schemePlusHost, String username,
937 String password, final Message resumeMsg) {
938 boolean rVal = false;
939 if (resumeMsg == null) {
940 // null resumeMsg implies saving password silently
941 mDatabase.setUsernamePassword(schemePlusHost, username, password);
942 } else {
943 final Message remember = mPrivateHandler.obtainMessage(
944 REMEMBER_PASSWORD);
945 remember.getData().putString("host", schemePlusHost);
946 remember.getData().putString("username", username);
947 remember.getData().putString("password", password);
948 remember.obj = resumeMsg;
949
950 final Message neverRemember = mPrivateHandler.obtainMessage(
951 NEVER_REMEMBER_PASSWORD);
952 neverRemember.getData().putString("host", schemePlusHost);
953 neverRemember.getData().putString("username", username);
954 neverRemember.getData().putString("password", password);
955 neverRemember.obj = resumeMsg;
956
957 new AlertDialog.Builder(getContext())
958 .setTitle(com.android.internal.R.string.save_password_label)
959 .setMessage(com.android.internal.R.string.save_password_message)
960 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
961 new DialogInterface.OnClickListener() {
962 public void onClick(DialogInterface dialog, int which) {
963 resumeMsg.sendToTarget();
964 }
965 })
966 .setNeutralButton(com.android.internal.R.string.save_password_remember,
967 new DialogInterface.OnClickListener() {
968 public void onClick(DialogInterface dialog, int which) {
969 remember.sendToTarget();
970 }
971 })
972 .setNegativeButton(com.android.internal.R.string.save_password_never,
973 new DialogInterface.OnClickListener() {
974 public void onClick(DialogInterface dialog, int which) {
975 neverRemember.sendToTarget();
976 }
977 })
978 .setOnCancelListener(new OnCancelListener() {
979 public void onCancel(DialogInterface dialog) {
980 resumeMsg.sendToTarget();
981 }
982 }).show();
983 // Return true so that WebViewCore will pause while the dialog is
984 // up.
985 rVal = true;
986 }
987 return rVal;
988 }
989
990 @Override
991 public void setScrollBarStyle(int style) {
992 if (style == View.SCROLLBARS_INSIDE_INSET
993 || style == View.SCROLLBARS_OUTSIDE_INSET) {
994 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
995 } else {
996 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
997 }
998 super.setScrollBarStyle(style);
999 }
1000
1001 /**
1002 * Specify whether the horizontal scrollbar has overlay style.
1003 * @param overlay TRUE if horizontal scrollbar should have overlay style.
1004 */
1005 public void setHorizontalScrollbarOverlay(boolean overlay) {
1006 mOverlayHorizontalScrollbar = overlay;
1007 }
1008
1009 /**
1010 * Specify whether the vertical scrollbar has overlay style.
1011 * @param overlay TRUE if vertical scrollbar should have overlay style.
1012 */
1013 public void setVerticalScrollbarOverlay(boolean overlay) {
1014 mOverlayVerticalScrollbar = overlay;
1015 }
1016
1017 /**
1018 * Return whether horizontal scrollbar has overlay style
1019 * @return TRUE if horizontal scrollbar has overlay style.
1020 */
1021 public boolean overlayHorizontalScrollbar() {
1022 return mOverlayHorizontalScrollbar;
1023 }
1024
1025 /**
1026 * Return whether vertical scrollbar has overlay style
1027 * @return TRUE if vertical scrollbar has overlay style.
1028 */
1029 public boolean overlayVerticalScrollbar() {
1030 return mOverlayVerticalScrollbar;
1031 }
1032
1033 /*
1034 * Return the width of the view where the content of WebView should render
1035 * to.
Grace Kloba6ed525e2009-09-17 15:31:12 -07001036 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 */
Grace Kloba6ed525e2009-09-17 15:31:12 -07001038 /* package */ int getViewWidth() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
1040 return getWidth();
1041 } else {
1042 return getWidth() - getVerticalScrollbarWidth();
1043 }
1044 }
1045
1046 /*
Mike Reede8853fc2009-09-04 14:01:48 -04001047 * returns the height of the titlebarview (if any). Does not care about
1048 * scrolling
1049 */
1050 private int getTitleHeight() {
1051 return mTitleBar != null ? mTitleBar.getHeight() : 0;
1052 }
1053
1054 /*
1055 * Return the amount of the titlebarview (if any) that is visible
1056 */
1057 private int getVisibleTitleHeight() {
Grace Klobad7625dd2010-03-04 11:46:12 -08001058 // need to restrict mScrollY due to over scroll
1059 return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04001060 }
1061
1062 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 * Return the height of the view where the content of WebView should render
Leon Scroggins0236e672009-09-02 21:12:08 -04001064 * to. Note that this excludes mTitleBar, if there is one.
Grace Kloba6ed525e2009-09-17 15:31:12 -07001065 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 */
Grace Kloba6ed525e2009-09-17 15:31:12 -07001067 /* package */ int getViewHeight() {
Grace Kloba8eff73f2009-09-24 09:34:32 -07001068 return getViewHeightWithTitle() - getVisibleTitleHeight();
1069 }
1070
1071 private int getViewHeightWithTitle() {
Leon Scroggins0236e672009-09-02 21:12:08 -04001072 int height = getHeight();
Mike Reede8853fc2009-09-04 14:01:48 -04001073 if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001074 height -= getHorizontalScrollbarHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 }
Grace Kloba8eff73f2009-09-24 09:34:32 -07001076 return height;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
1078
1079 /**
1080 * @return The SSL certificate for the main top-level page or null if
1081 * there is no certificate (the site is not secure).
1082 */
1083 public SslCertificate getCertificate() {
1084 return mCertificate;
1085 }
1086
1087 /**
1088 * Sets the SSL certificate for the main top-level page.
1089 */
1090 public void setCertificate(SslCertificate certificate) {
Brian Carlstromdba8cb72010-03-18 16:56:41 -07001091 if (DebugFlags.WEB_VIEW) {
1092 Log.v(LOGTAG, "setCertificate=" + certificate);
1093 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 // here, the certificate can be null (if the site is not secure)
1095 mCertificate = certificate;
1096 }
1097
1098 //-------------------------------------------------------------------------
1099 // Methods called by activity
1100 //-------------------------------------------------------------------------
1101
1102 /**
1103 * Save the username and password for a particular host in the WebView's
1104 * internal database.
1105 * @param host The host that required the credentials.
1106 * @param username The username for the given host.
1107 * @param password The password for the given host.
1108 */
1109 public void savePassword(String host, String username, String password) {
1110 mDatabase.setUsernamePassword(host, username, password);
1111 }
1112
1113 /**
1114 * Set the HTTP authentication credentials for a given host and realm.
1115 *
1116 * @param host The host for the credentials.
1117 * @param realm The realm for the credentials.
1118 * @param username The username for the password. If it is null, it means
1119 * password can't be saved.
1120 * @param password The password
1121 */
1122 public void setHttpAuthUsernamePassword(String host, String realm,
1123 String username, String password) {
1124 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
1125 }
1126
1127 /**
1128 * Retrieve the HTTP authentication username and password for a given
1129 * host & realm pair
1130 *
1131 * @param host The host for which the credentials apply.
1132 * @param realm The realm for which the credentials apply.
1133 * @return String[] if found, String[0] is username, which can be null and
1134 * String[1] is password. Return null if it can't find anything.
1135 */
1136 public String[] getHttpAuthUsernamePassword(String host, String realm) {
1137 return mDatabase.getHttpAuthUsernamePassword(host, realm);
1138 }
1139
1140 /**
1141 * Destroy the internal state of the WebView. This method should be called
1142 * after the WebView has been removed from the view system. No other
1143 * methods may be called on a WebView after destroy.
1144 */
1145 public void destroy() {
Leon Scroggins6088e832010-02-17 13:17:32 -05001146 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 if (mWebViewCore != null) {
1148 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -04001149 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 mCallbackProxy.setWebViewClient(null);
1151 mCallbackProxy.setWebChromeClient(null);
1152 // Tell WebViewCore to destroy itself
Andrei Popescu04098572010-03-09 12:23:19 +00001153 synchronized (this) {
1154 WebViewCore webViewCore = mWebViewCore;
1155 mWebViewCore = null; // prevent using partial webViewCore
1156 webViewCore.destroy();
1157 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 // Remove any pending messages that might not be serviced yet.
1159 mPrivateHandler.removeCallbacksAndMessages(null);
1160 mCallbackProxy.removeCallbacksAndMessages(null);
1161 // Wake up the WebCore thread just in case it is waiting for a
1162 // javascript dialog.
1163 synchronized (mCallbackProxy) {
1164 mCallbackProxy.notify();
1165 }
1166 }
1167 if (mNativeClass != 0) {
1168 nativeDestroy();
1169 mNativeClass = 0;
1170 }
1171 }
1172
1173 /**
1174 * Enables platform notifications of data state and proxy changes.
1175 */
1176 public static void enablePlatformNotifications() {
1177 Network.enablePlatformNotifications();
1178 }
1179
1180 /**
1181 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001182 * from the Activity's onPause() or onStop().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001183 */
1184 public static void disablePlatformNotifications() {
1185 Network.disablePlatformNotifications();
1186 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001187
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 /**
Feng Qianb3081372009-06-29 15:55:18 -07001189 * Sets JavaScript engine flags.
1190 *
1191 * @param flags JS engine flags in a String
1192 *
1193 * @hide pending API solidification
1194 */
1195 public void setJsFlags(String flags) {
1196 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1197 }
1198
1199 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 * Inform WebView of the network state. This is used to set
1201 * the javascript property window.navigator.isOnline and
1202 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1203 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001204 */
1205 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001206 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1207 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001208 }
1209
1210 /**
Andrei Popescuf5dba882010-01-12 22:42:41 +00001211 * Inform WebView about the current network type.
1212 * {@hide}
1213 */
1214 public void setNetworkType(String type, String subtype) {
1215 Map<String, String> map = new HashMap<String, String>();
1216 map.put("type", type);
1217 map.put("subtype", subtype);
1218 mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
1219 }
1220 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001221 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001222 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1223 * method no longer stores the display data for this WebView. The previous
1224 * behavior could potentially leak files if {@link #restoreState} was never
1225 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1226 * and restoring the display data.
1227 * @param outState The Bundle to store the WebView state.
1228 * @return The same copy of the back/forward list used to save the state. If
1229 * saveState fails, the returned list will be null.
1230 * @see #savePicture
1231 * @see #restorePicture
1232 */
1233 public WebBackForwardList saveState(Bundle outState) {
1234 if (outState == null) {
1235 return null;
1236 }
1237 // We grab a copy of the back/forward list because a client of WebView
1238 // may have invalidated the history list by calling clearHistory.
1239 WebBackForwardList list = copyBackForwardList();
1240 final int currentIndex = list.getCurrentIndex();
1241 final int size = list.getSize();
1242 // We should fail saving the state if the list is empty or the index is
1243 // not in a valid range.
1244 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1245 return null;
1246 }
1247 outState.putInt("index", currentIndex);
1248 // FIXME: This should just be a byte[][] instead of ArrayList but
1249 // Parcel.java does not have the code to handle multi-dimensional
1250 // arrays.
1251 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1252 for (int i = 0; i < size; i++) {
1253 WebHistoryItem item = list.getItemAtIndex(i);
Cary Clark4fbf81b2009-09-29 16:07:56 -04001254 if (null == item) {
1255 // FIXME: this shouldn't happen
1256 // need to determine how item got set to null
1257 Log.w(LOGTAG, "saveState: Unexpected null history item.");
1258 return null;
1259 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260 byte[] data = item.getFlattenedData();
1261 if (data == null) {
1262 // It would be very odd to not have any data for a given history
1263 // item. And we will fail to rebuild the history list without
1264 // flattened data.
1265 return null;
1266 }
1267 history.add(data);
1268 }
1269 outState.putSerializable("history", history);
1270 if (mCertificate != null) {
1271 outState.putBundle("certificate",
1272 SslCertificate.saveState(mCertificate));
1273 }
1274 return list;
1275 }
1276
1277 /**
1278 * Save the current display data to the Bundle given. Used in conjunction
1279 * with {@link #saveState}.
1280 * @param b A Bundle to store the display data.
1281 * @param dest The file to store the serialized picture data. Will be
1282 * overwritten with this WebView's picture data.
1283 * @return True if the picture was successfully saved.
1284 */
1285 public boolean savePicture(Bundle b, File dest) {
1286 if (dest == null || b == null) {
1287 return false;
1288 }
1289 final Picture p = capturePicture();
1290 try {
1291 final FileOutputStream out = new FileOutputStream(dest);
1292 p.writeToStream(out);
1293 out.close();
Mike Reed189e7352010-03-05 10:12:29 -05001294 // now update the bundle
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001295 b.putInt("scrollX", mScrollX);
1296 b.putInt("scrollY", mScrollY);
1297 b.putFloat("scale", mActualScale);
Grace Kloba3a0def22010-01-23 21:11:54 -08001298 b.putFloat("textwrapScale", mTextWrapScale);
1299 b.putBoolean("overview", mInZoomOverview);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001300 return true;
Mike Reed189e7352010-03-05 10:12:29 -05001301 } catch (FileNotFoundException e){
1302 e.printStackTrace();
1303 } catch (IOException e) {
1304 e.printStackTrace();
1305 } catch (RuntimeException e) {
1306 e.printStackTrace();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 }
1308 return false;
1309 }
1310
1311 /**
1312 * Restore the display data that was save in {@link #savePicture}. Used in
1313 * conjunction with {@link #restoreState}.
1314 * @param b A Bundle containing the saved display data.
1315 * @param src The file where the picture data was stored.
1316 * @return True if the picture was successfully restored.
1317 */
1318 public boolean restorePicture(Bundle b, File src) {
1319 if (src == null || b == null) {
1320 return false;
1321 }
1322 if (src.exists()) {
1323 Picture p = null;
1324 try {
1325 final FileInputStream in = new FileInputStream(src);
1326 p = Picture.createFromStream(in);
1327 in.close();
1328 } catch (FileNotFoundException e){
1329 e.printStackTrace();
1330 } catch (RuntimeException e) {
1331 e.printStackTrace();
1332 } catch (IOException e) {
1333 e.printStackTrace();
1334 }
1335 if (p != null) {
1336 int sx = b.getInt("scrollX", 0);
1337 int sy = b.getInt("scrollY", 0);
1338 float scale = b.getFloat("scale", 1.0f);
1339 mDrawHistory = true;
1340 mHistoryPicture = p;
1341 mScrollX = sx;
1342 mScrollY = sy;
1343 mHistoryWidth = Math.round(p.getWidth() * scale);
1344 mHistoryHeight = Math.round(p.getHeight() * scale);
1345 // as getWidth() / getHeight() of the view are not
1346 // available yet, set up mActualScale, so that when
1347 // onSizeChanged() is called, the rest will be set
1348 // correctly
1349 mActualScale = scale;
Cary Clark2ec30692010-02-23 10:50:38 -05001350 mInvActualScale = 1 / scale;
Grace Kloba3a0def22010-01-23 21:11:54 -08001351 mTextWrapScale = b.getFloat("textwrapScale", scale);
1352 mInZoomOverview = b.getBoolean("overview");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001353 invalidate();
1354 return true;
1355 }
1356 }
1357 return false;
1358 }
1359
1360 /**
1361 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001362 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1363 * be called to restore the state of the WebView before using the object. If
1364 * it is called after the WebView has had a chance to build state (load
1365 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001366 * side-effects. Please note that this method no longer restores the
1367 * display data for this WebView. See {@link #savePicture} and {@link
1368 * #restorePicture} for saving and restoring the display data.
1369 * @param inState The incoming Bundle of state.
1370 * @return The restored back/forward list or null if restoreState failed.
1371 * @see #savePicture
1372 * @see #restorePicture
1373 */
1374 public WebBackForwardList restoreState(Bundle inState) {
1375 WebBackForwardList returnList = null;
1376 if (inState == null) {
1377 return returnList;
1378 }
1379 if (inState.containsKey("index") && inState.containsKey("history")) {
1380 mCertificate = SslCertificate.restoreState(
1381 inState.getBundle("certificate"));
1382
1383 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1384 final int index = inState.getInt("index");
1385 // We can't use a clone of the list because we need to modify the
1386 // shared copy, so synchronize instead to prevent concurrent
1387 // modifications.
1388 synchronized (list) {
1389 final List<byte[]> history =
1390 (List<byte[]>) inState.getSerializable("history");
1391 final int size = history.size();
1392 // Check the index bounds so we don't crash in native code while
1393 // restoring the history index.
1394 if (index < 0 || index >= size) {
1395 return null;
1396 }
1397 for (int i = 0; i < size; i++) {
1398 byte[] data = history.remove(0);
1399 if (data == null) {
1400 // If we somehow have null data, we cannot reconstruct
1401 // the item and thus our history list cannot be rebuilt.
1402 return null;
1403 }
1404 WebHistoryItem item = new WebHistoryItem(data);
1405 list.addHistoryItem(item);
1406 }
1407 // Grab the most recent copy to return to the caller.
1408 returnList = copyBackForwardList();
1409 // Update the copy to have the correct index.
1410 returnList.setCurrentIndex(index);
1411 }
1412 // Remove all pending messages because we are restoring previous
1413 // state.
1414 mWebViewCore.removeMessages();
1415 // Send a restore state message.
1416 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1417 }
1418 return returnList;
1419 }
1420
1421 /**
Grace Klobad0d9bc22010-01-26 18:08:28 -08001422 * Load the given url with the extra headers.
1423 * @param url The url of the resource to load.
1424 * @param extraHeaders The extra headers sent with this url. This should not
1425 * include the common headers like "user-agent". If it does, it
1426 * will be replaced by the intrinsic value of the WebView.
1427 */
1428 public void loadUrl(String url, Map<String, String> extraHeaders) {
1429 switchOutDrawHistory();
1430 WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
1431 arg.mUrl = url;
1432 arg.mExtraHeaders = extraHeaders;
1433 mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
Leon Scroggins6088e832010-02-17 13:17:32 -05001434 clearTextEntry(false);
Grace Klobad0d9bc22010-01-26 18:08:28 -08001435 }
1436
1437 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 * Load the given url.
1439 * @param url The url of the resource to load.
1440 */
1441 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001442 if (url == null) {
1443 return;
1444 }
Grace Klobad0d9bc22010-01-26 18:08:28 -08001445 loadUrl(url, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001446 }
1447
1448 /**
Grace Kloba57534302009-05-22 18:55:02 -07001449 * Load the url with postData using "POST" method into the WebView. If url
1450 * is not a network url, it will be loaded with {link
1451 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001452 *
Grace Kloba57534302009-05-22 18:55:02 -07001453 * @param url The url of the resource to load.
1454 * @param postData The data will be passed to "POST" request.
Grace Kloba57534302009-05-22 18:55:02 -07001455 */
1456 public void postUrl(String url, byte[] postData) {
1457 if (URLUtil.isNetworkUrl(url)) {
1458 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001459 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1460 arg.mUrl = url;
1461 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001462 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
Leon Scroggins6088e832010-02-17 13:17:32 -05001463 clearTextEntry(false);
Grace Kloba57534302009-05-22 18:55:02 -07001464 } else {
1465 loadUrl(url);
1466 }
1467 }
1468
1469 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001470 * Load the given data into the WebView. This will load the data into
1471 * WebView using the data: scheme. Content loaded through this mechanism
1472 * does not have the ability to load content from the network.
Cary Clarkfc2ece42010-03-15 16:01:49 -04001473 * @param data A String of data in the given encoding. The date must
1474 * be URI-escaped -- '#', '%', '\', '?' should be replaced by %23, %25,
1475 * %27, %3f respectively.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001476 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1477 * @param encoding The encoding of the data. i.e. utf-8, base64
1478 */
1479 public void loadData(String data, String mimeType, String encoding) {
1480 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1481 }
1482
1483 /**
1484 * Load the given data into the WebView, use the provided URL as the base
1485 * URL for the content. The base URL is the URL that represents the page
1486 * that is loaded through this interface. As such, it is used for the
1487 * history entry and to resolve any relative URLs. The failUrl is used if
1488 * browser fails to load the data provided. If it is empty or null, and the
1489 * load fails, then no history entry is created.
1490 * <p>
1491 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1492 * files through "file:///android_asset/" for the sub resources is more
1493 * restricted. If you provide null or empty string as baseUrl, you won't be
1494 * able to access asset files. If the baseUrl is anything other than
1495 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1496 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001497 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001498 * @param baseUrl Url to resolve relative paths with, if null defaults to
1499 * "about:blank"
1500 * @param data A String of data in the given encoding.
1501 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1502 * defaults to "text/html"
1503 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
1504 * @param failUrl URL to use if the content fails to load or null.
1505 */
1506 public void loadDataWithBaseURL(String baseUrl, String data,
1507 String mimeType, String encoding, String failUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001508
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001509 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1510 loadData(data, mimeType, encoding);
1511 return;
1512 }
1513 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001514 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1515 arg.mBaseUrl = baseUrl;
1516 arg.mData = data;
1517 arg.mMimeType = mimeType;
1518 arg.mEncoding = encoding;
1519 arg.mFailUrl = failUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
Leon Scroggins6088e832010-02-17 13:17:32 -05001521 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001522 }
1523
1524 /**
1525 * Stop the current load.
1526 */
1527 public void stopLoading() {
1528 // TODO: should we clear all the messages in the queue before sending
1529 // STOP_LOADING?
1530 switchOutDrawHistory();
1531 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1532 }
1533
1534 /**
1535 * Reload the current url.
1536 */
1537 public void reload() {
Leon Scroggins6088e832010-02-17 13:17:32 -05001538 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001539 switchOutDrawHistory();
1540 mWebViewCore.sendMessage(EventHub.RELOAD);
1541 }
1542
1543 /**
1544 * Return true if this WebView has a back history item.
1545 * @return True iff this WebView has a back history item.
1546 */
1547 public boolean canGoBack() {
1548 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1549 synchronized (l) {
1550 if (l.getClearPending()) {
1551 return false;
1552 } else {
1553 return l.getCurrentIndex() > 0;
1554 }
1555 }
1556 }
1557
1558 /**
1559 * Go back in the history of this WebView.
1560 */
1561 public void goBack() {
1562 goBackOrForward(-1);
1563 }
1564
1565 /**
1566 * Return true if this WebView has a forward history item.
1567 * @return True iff this Webview has a forward history item.
1568 */
1569 public boolean canGoForward() {
1570 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1571 synchronized (l) {
1572 if (l.getClearPending()) {
1573 return false;
1574 } else {
1575 return l.getCurrentIndex() < l.getSize() - 1;
1576 }
1577 }
1578 }
1579
1580 /**
1581 * Go forward in the history of this WebView.
1582 */
1583 public void goForward() {
1584 goBackOrForward(1);
1585 }
1586
1587 /**
1588 * Return true if the page can go back or forward the given
1589 * number of steps.
1590 * @param steps The negative or positive number of steps to move the
1591 * history.
1592 */
1593 public boolean canGoBackOrForward(int steps) {
1594 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1595 synchronized (l) {
1596 if (l.getClearPending()) {
1597 return false;
1598 } else {
1599 int newIndex = l.getCurrentIndex() + steps;
1600 return newIndex >= 0 && newIndex < l.getSize();
1601 }
1602 }
1603 }
1604
1605 /**
1606 * Go to the history item that is the number of steps away from
1607 * the current item. Steps is negative if backward and positive
1608 * if forward.
1609 * @param steps The number of steps to take back or forward in the back
1610 * forward list.
1611 */
1612 public void goBackOrForward(int steps) {
1613 goBackOrForward(steps, false);
1614 }
1615
1616 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001617 if (steps != 0) {
Leon Scroggins6088e832010-02-17 13:17:32 -05001618 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001619 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
1620 ignoreSnapshot ? 1 : 0);
1621 }
1622 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001623
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001624 private boolean extendScroll(int y) {
1625 int finalY = mScroller.getFinalY();
1626 int newY = pinLocY(finalY + y);
1627 if (newY == finalY) return false;
1628 mScroller.setFinalY(newY);
1629 mScroller.extendDuration(computeDuration(0, y));
1630 return true;
1631 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001632
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001633 /**
1634 * Scroll the contents of the view up by half the view size
1635 * @param top true to jump to the top of the page
1636 * @return true if the page was scrolled
1637 */
1638 public boolean pageUp(boolean top) {
1639 if (mNativeClass == 0) {
1640 return false;
1641 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001642 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001643 if (top) {
1644 // go to the top of the document
1645 return pinScrollTo(mScrollX, 0, true, 0);
1646 }
1647 // Page up
1648 int h = getHeight();
1649 int y;
1650 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1651 y = -h + PAGE_SCROLL_OVERLAP;
1652 } else {
1653 y = -h / 2;
1654 }
1655 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001656 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 : extendScroll(y);
1658 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001659
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001660 /**
1661 * Scroll the contents of the view down by half the page size
1662 * @param bottom true to jump to bottom of page
1663 * @return true if the page was scrolled
1664 */
1665 public boolean pageDown(boolean bottom) {
1666 if (mNativeClass == 0) {
1667 return false;
1668 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001669 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001670 if (bottom) {
Grace Klobad7439f42009-11-10 14:11:33 -08001671 return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001672 }
1673 // Page down.
1674 int h = getHeight();
1675 int y;
1676 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1677 y = h - PAGE_SCROLL_OVERLAP;
1678 } else {
1679 y = h / 2;
1680 }
1681 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001682 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001683 : extendScroll(y);
1684 }
1685
1686 /**
1687 * Clear the view so that onDraw() will draw nothing but white background,
1688 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
1689 */
1690 public void clearView() {
1691 mContentWidth = 0;
1692 mContentHeight = 0;
1693 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
1694 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001695
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001696 /**
1697 * Return a new picture that captures the current display of the webview.
1698 * This is a copy of the display, and will be unaffected if the webview
1699 * later loads a different URL.
1700 *
1701 * @return a picture containing the current contents of the view. Note this
1702 * picture is of the entire document, and is not restricted to the
1703 * bounds of the view.
1704 */
1705 public Picture capturePicture() {
Cary Clarkd6982c92009-05-29 11:02:22 -04001706 if (null == mWebViewCore) return null; // check for out of memory tab
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001707 return mWebViewCore.copyContentPicture();
1708 }
1709
1710 /**
1711 * Return true if the browser is displaying a TextView for text input.
1712 */
1713 private boolean inEditingMode() {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001714 return mWebTextView != null && mWebTextView.getParent() != null
1715 && mWebTextView.hasFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001716 }
1717
Leon Scroggins6088e832010-02-17 13:17:32 -05001718 /**
1719 * Remove the WebTextView.
1720 * @param disableFocusController If true, send a message to webkit
1721 * disabling the focus controller, so the caret stops blinking.
1722 */
1723 private void clearTextEntry(boolean disableFocusController) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001724 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001725 mWebTextView.remove();
Leon Scroggins6088e832010-02-17 13:17:32 -05001726 if (disableFocusController) {
1727 setFocusControllerInactive();
1728 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001729 }
1730 }
1731
Cary Clarkd6982c92009-05-29 11:02:22 -04001732 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001733 * Return the current scale of the WebView
1734 * @return The current scale.
1735 */
1736 public float getScale() {
1737 return mActualScale;
1738 }
1739
1740 /**
1741 * Set the initial scale for the WebView. 0 means default. If
1742 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
1743 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
1744 * WebView starts will this value as initial scale.
1745 *
1746 * @param scaleInPercent The initial scale in percent.
1747 */
1748 public void setInitialScale(int scaleInPercent) {
Grace Kloba16efce72009-11-10 15:49:03 -08001749 mInitialScaleInPercent = scaleInPercent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001750 }
1751
1752 /**
1753 * Invoke the graphical zoom picker widget for this WebView. This will
1754 * result in the zoom widget appearing on the screen to control the zoom
1755 * level of this WebView.
1756 */
1757 public void invokeZoomPicker() {
1758 if (!getSettings().supportZoom()) {
1759 Log.w(LOGTAG, "This WebView doesn't support zoom.");
1760 return;
1761 }
Leon Scroggins6088e832010-02-17 13:17:32 -05001762 clearTextEntry(false);
The Android Open Source Project10592532009-03-18 17:39:46 -07001763 if (getSettings().getBuiltInZoomControls()) {
1764 mZoomButtonsController.setVisible(true);
1765 } else {
1766 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
1767 mPrivateHandler.postDelayed(mZoomControlRunnable,
1768 ZOOM_CONTROLS_TIMEOUT);
1769 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001770 }
1771
1772 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001773 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001774 * is found and the anchor has a non-javascript url, the HitTestResult type
1775 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
1776 * anchor does not have a url or if it is a javascript url, the type will
1777 * be UNKNOWN_TYPE and the url has to be retrieved through
1778 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
1779 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
1780 * the "extra" field. A type of
1781 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
1782 * a child node. If a phone number is found, the HitTestResult type is set
1783 * to PHONE_TYPE and the phone number is set in the "extra" field of
1784 * HitTestResult. If a map address is found, the HitTestResult type is set
1785 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
1786 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
1787 * and the email is set in the "extra" field of HitTestResult. Otherwise,
1788 * HitTestResult type is set to UNKNOWN_TYPE.
1789 */
1790 public HitTestResult getHitTestResult() {
1791 if (mNativeClass == 0) {
1792 return null;
1793 }
1794
1795 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04001796 if (nativeHasCursorNode()) {
1797 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001798 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04001799 } else {
1800 String text = nativeCursorText();
1801 if (text != null) {
1802 if (text.startsWith(SCHEME_TEL)) {
1803 result.setType(HitTestResult.PHONE_TYPE);
1804 result.setExtra(text.substring(SCHEME_TEL.length()));
1805 } else if (text.startsWith(SCHEME_MAILTO)) {
1806 result.setType(HitTestResult.EMAIL_TYPE);
1807 result.setExtra(text.substring(SCHEME_MAILTO.length()));
1808 } else if (text.startsWith(SCHEME_GEO)) {
1809 result.setType(HitTestResult.GEO_TYPE);
1810 result.setExtra(URLDecoder.decode(text
1811 .substring(SCHEME_GEO.length())));
1812 } else if (nativeCursorIsAnchor()) {
1813 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
1814 result.setExtra(text);
1815 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 }
1817 }
1818 }
1819 int type = result.getType();
1820 if (type == HitTestResult.UNKNOWN_TYPE
1821 || type == HitTestResult.SRC_ANCHOR_TYPE) {
1822 // Now check to see if it is an image.
Leon Scroggins0236e672009-09-02 21:12:08 -04001823 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1824 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001825 String text = nativeImageURI(contentX, contentY);
1826 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001827 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
1828 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001829 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
1830 result.setExtra(text);
1831 }
1832 }
1833 return result;
1834 }
1835
Leon Scrogginse26efa32009-12-15 16:38:45 -05001836 // Called by JNI when the DOM has changed the focus. Clear the focus so
1837 // that new keys will go to the newly focused field
1838 private void domChangedFocus() {
1839 if (inEditingMode()) {
1840 mPrivateHandler.obtainMessage(DOM_FOCUS_CHANGED).sendToTarget();
1841 }
1842 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001843 /**
1844 * Request the href of an anchor element due to getFocusNodePath returning
1845 * "href." If hrefMsg is null, this method returns immediately and does not
1846 * dispatch hrefMsg to its target.
Cary Clarkd6982c92009-05-29 11:02:22 -04001847 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 * @param hrefMsg This message will be dispatched with the result of the
1849 * request as the data member with "url" as key. The result can
1850 * be null.
1851 */
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001852 // FIXME: API change required to change the name of this function. We now
1853 // look at the cursor node, and not the focus node. Also, what is
1854 // getFocusNodePath?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001855 public void requestFocusNodeHref(Message hrefMsg) {
1856 if (hrefMsg == null || mNativeClass == 0) {
1857 return;
1858 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001859 if (nativeCursorIsAnchor()) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001860 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
Cary Clarkd6982c92009-05-29 11:02:22 -04001861 nativeCursorFramePointer(), nativeCursorNodePointer(),
1862 hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863 }
1864 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001865
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 /**
1867 * Request the url of the image last touched by the user. msg will be sent
1868 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04001869 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001870 * @param msg This message will be dispatched with the result of the request
1871 * as the data member with "url" as key. The result can be null.
1872 */
1873 public void requestImageRef(Message msg) {
Cary Clark7f970112009-10-15 15:29:08 -04001874 if (0 == mNativeClass) return; // client isn't initialized
Leon Scroggins0236e672009-09-02 21:12:08 -04001875 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1876 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001877 String ref = nativeImageURI(contentX, contentY);
1878 Bundle data = msg.getData();
1879 data.putString("url", ref);
1880 msg.setData(data);
1881 msg.sendToTarget();
1882 }
1883
1884 private static int pinLoc(int x, int viewMax, int docMax) {
1885// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
1886 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001887 // pin the short document to the top/left of the screen
1888 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001889// Log.d(LOGTAG, "--- center " + x);
1890 } else if (x < 0) {
1891 x = 0;
1892// Log.d(LOGTAG, "--- zero");
1893 } else if (x + viewMax > docMax) {
1894 x = docMax - viewMax;
1895// Log.d(LOGTAG, "--- pin " + x);
1896 }
1897 return x;
1898 }
1899
1900 // Expects x in view coordinates
1901 private int pinLocX(int x) {
Grace Klobad7625dd2010-03-04 11:46:12 -08001902 if (mInOverScrollMode) return x;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001903 return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
1904 }
1905
1906 // Expects y in view coordinates
1907 private int pinLocY(int y) {
Grace Klobad7625dd2010-03-04 11:46:12 -08001908 if (mInOverScrollMode) return y;
Mike Reede5e63f42010-03-19 14:38:23 -04001909 return pinLoc(y, getViewHeightWithTitle(),
1910 computeVerticalScrollRange() + getTitleHeight());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001911 }
1912
Leon Scroggins0236e672009-09-02 21:12:08 -04001913 /**
1914 * A title bar which is embedded in this WebView, and scrolls along with it
1915 * vertically, but not horizontally.
1916 */
1917 private View mTitleBar;
1918
1919 /**
Leon Scroggins58992ea2009-09-17 15:55:31 -04001920 * Since we draw the title bar ourselves, we removed the shadow from the
1921 * browser's activity. We do want a shadow at the bottom of the title bar,
1922 * or at the top of the screen if the title bar is not visible. This
1923 * drawable serves that purpose.
1924 */
1925 private Drawable mTitleShadow;
1926
1927 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001928 * Add or remove a title bar to be embedded into the WebView, and scroll
1929 * along with it vertically, while remaining in view horizontally. Pass
1930 * null to remove the title bar from the WebView, and return to drawing
1931 * the WebView normally without translating to account for the title bar.
1932 * @hide
1933 */
Leon Scroggins078c52c2009-09-04 16:58:09 -04001934 public void setEmbeddedTitleBar(View v) {
1935 if (mTitleBar == v) return;
1936 if (mTitleBar != null) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001937 removeView(mTitleBar);
Leon Scroggins078c52c2009-09-04 16:58:09 -04001938 }
1939 if (null != v) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001940 addView(v, new AbsoluteLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001941 ViewGroup.LayoutParams.MATCH_PARENT,
Leon Scroggins078c52c2009-09-04 16:58:09 -04001942 ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
Leon Scroggins58992ea2009-09-17 15:55:31 -04001943 if (mTitleShadow == null) {
1944 mTitleShadow = (Drawable) mContext.getResources().getDrawable(
1945 com.android.internal.R.drawable.title_bar_shadow);
1946 }
Leon Scroggins0236e672009-09-02 21:12:08 -04001947 }
1948 mTitleBar = v;
1949 }
1950
1951 /**
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001952 * Given a distance in view space, convert it to content space. Note: this
1953 * does not reflect translation, just scaling, so this should not be called
1954 * with coordinates, but should be called for dimensions like width or
1955 * height.
1956 */
1957 private int viewToContentDimension(int d) {
1958 return Math.round(d * mInvActualScale);
1959 }
1960
1961 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001962 * Given an x coordinate in view space, convert it to content space. Also
1963 * may be used for absolute heights (such as for the WebTextView's
1964 * textSize, which is unaffected by the height of the title bar).
1965 */
1966 /*package*/ int viewToContentX(int x) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001967 return viewToContentDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001968 }
1969
Leon Scroggins0236e672009-09-02 21:12:08 -04001970 /**
1971 * Given a y coordinate in view space, convert it to content space.
1972 * Takes into account the height of the title bar if there is one
1973 * embedded into the WebView.
1974 */
1975 /*package*/ int viewToContentY(int y) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001976 return viewToContentDimension(y - getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04001977 }
1978
1979 /**
1980 * Given a distance in content space, convert it to view space. Note: this
1981 * does not reflect translation, just scaling, so this should not be called
1982 * with coordinates, but should be called for dimensions like width or
1983 * height.
1984 */
1985 /*package*/ int contentToViewDimension(int d) {
1986 return Math.round(d * mActualScale);
Leon Scroggins0236e672009-09-02 21:12:08 -04001987 }
1988
1989 /**
1990 * Given an x coordinate in content space, convert it to view
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001991 * space.
Leon Scroggins0236e672009-09-02 21:12:08 -04001992 */
1993 /*package*/ int contentToViewX(int x) {
Mike Reede8853fc2009-09-04 14:01:48 -04001994 return contentToViewDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001995 }
1996
Leon Scroggins0236e672009-09-02 21:12:08 -04001997 /**
1998 * Given a y coordinate in content space, convert it to view
1999 * space. Takes into account the height of the title bar.
2000 */
2001 /*package*/ int contentToViewY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04002002 return contentToViewDimension(y) + getTitleHeight();
Leon Scroggins0236e672009-09-02 21:12:08 -04002003 }
2004
Mike Reede9e86b82009-09-15 11:26:53 -04002005 private Rect contentToViewRect(Rect x) {
2006 return new Rect(contentToViewX(x.left), contentToViewY(x.top),
2007 contentToViewX(x.right), contentToViewY(x.bottom));
2008 }
2009
2010 /* To invalidate a rectangle in content coordinates, we need to transform
2011 the rect into view coordinates, so we can then call invalidate(...).
2012
2013 Normally, we would just call contentToView[XY](...), which eventually
2014 calls Math.round(coordinate * mActualScale). However, for invalidates,
2015 we need to account for the slop that occurs with antialiasing. To
2016 address that, we are a little more liberal in the size of the rect that
2017 we invalidate.
2018
2019 This liberal calculation calls floor() for the top/left, and ceil() for
2020 the bottom/right coordinates. This catches the possible extra pixels of
2021 antialiasing that we might have missed with just round().
2022 */
2023
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002024 // Called by JNI to invalidate the View, given rectangle coordinates in
2025 // content space
2026 private void viewInvalidate(int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04002027 final float scale = mActualScale;
2028 final int dy = getTitleHeight();
2029 invalidate((int)Math.floor(l * scale),
2030 (int)Math.floor(t * scale) + dy,
2031 (int)Math.ceil(r * scale),
2032 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002033 }
2034
2035 // Called by JNI to invalidate the View after a delay, given rectangle
2036 // coordinates in content space
2037 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04002038 final float scale = mActualScale;
2039 final int dy = getTitleHeight();
2040 postInvalidateDelayed(delay,
2041 (int)Math.floor(l * scale),
2042 (int)Math.floor(t * scale) + dy,
2043 (int)Math.ceil(r * scale),
2044 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002045 }
2046
Mike Reede9e86b82009-09-15 11:26:53 -04002047 private void invalidateContentRect(Rect r) {
2048 viewInvalidate(r.left, r.top, r.right, r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002049 }
2050
Cary Clark278ce052009-08-31 16:08:42 -04002051 // stop the scroll animation, and don't let a subsequent fling add
2052 // to the existing velocity
2053 private void abortAnimation() {
2054 mScroller.abortAnimation();
2055 mLastVelocity = 0;
2056 }
2057
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002058 /* call from webcoreview.draw(), so we're still executing in the UI thread
2059 */
2060 private void recordNewContentSize(int w, int h, boolean updateLayout) {
2061
2062 // premature data from webkit, ignore
2063 if ((w | h) == 0) {
2064 return;
2065 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002066
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002067 // don't abort a scroll animation if we didn't change anything
2068 if (mContentWidth != w || mContentHeight != h) {
2069 // record new dimensions
2070 mContentWidth = w;
2071 mContentHeight = h;
2072 // If history Picture is drawn, don't update scroll. They will be
2073 // updated when we get out of that mode.
2074 if (!mDrawHistory) {
2075 // repin our scroll, taking into account the new content size
2076 int oldX = mScrollX;
2077 int oldY = mScrollY;
2078 mScrollX = pinLocX(mScrollX);
2079 mScrollY = pinLocY(mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002080 if (oldX != mScrollX || oldY != mScrollY) {
2081 sendOurVisibleRect();
2082 }
Leon Scrogginsd84e7d52009-09-29 11:11:45 -04002083 if (!mScroller.isFinished()) {
2084 // We are in the middle of a scroll. Repin the final scroll
2085 // position.
2086 mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
2087 mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
2088 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002089 }
2090 }
2091 contentSizeChanged(updateLayout);
2092 }
2093
Grace Kloba3a0def22010-01-23 21:11:54 -08002094 private void setNewZoomScale(float scale, boolean updateTextWrapScale,
2095 boolean force) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002096 if (scale < mMinZoomScale) {
2097 scale = mMinZoomScale;
Grace Klobab3d0cc52010-03-01 12:25:29 -08002098 // set mInZoomOverview for non mobile sites
2099 if (scale < mDefaultScale) mInZoomOverview = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002100 } else if (scale > mMaxZoomScale) {
2101 scale = mMaxZoomScale;
2102 }
Grace Kloba3a0def22010-01-23 21:11:54 -08002103 if (updateTextWrapScale) {
2104 mTextWrapScale = scale;
2105 // reset mLastHeightSent to force VIEW_SIZE_CHANGED sent to WebKit
2106 mLastHeightSent = 0;
2107 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002108 if (scale != mActualScale || force) {
2109 if (mDrawHistory) {
2110 // If history Picture is drawn, don't update scroll. They will
2111 // be updated when we get out of that mode.
2112 if (scale != mActualScale && !mPreviewZoomOnly) {
2113 mCallbackProxy.onScaleChanged(mActualScale, scale);
2114 }
2115 mActualScale = scale;
2116 mInvActualScale = 1 / scale;
Grace Kloba3a0def22010-01-23 21:11:54 -08002117 sendViewSizeZoom();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002118 } else {
2119 // update our scroll so we don't appear to jump
2120 // i.e. keep the center of the doc in the center of the view
2121
2122 int oldX = mScrollX;
2123 int oldY = mScrollY;
2124 float ratio = scale * mInvActualScale; // old inverse
2125 float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
Grace Klobac0c03af2009-09-17 11:01:44 -07002126 float sy = ratio * oldY + (ratio - 1)
2127 * (mZoomCenterY - getTitleHeight());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002128
2129 // now update our new scale and inverse
2130 if (scale != mActualScale && !mPreviewZoomOnly) {
2131 mCallbackProxy.onScaleChanged(mActualScale, scale);
2132 }
2133 mActualScale = scale;
2134 mInvActualScale = 1 / scale;
2135
Patrick Scott0a5ce012009-07-02 08:56:10 -04002136 // Scale all the child views
2137 mViewManager.scaleAll();
2138
Cary Clarkd6982c92009-05-29 11:02:22 -04002139 // as we don't have animation for scaling, don't do animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002140 // for scrolling, as it causes weird intermediate state
2141 // pinScrollTo(Math.round(sx), Math.round(sy));
2142 mScrollX = pinLocX(Math.round(sx));
2143 mScrollY = pinLocY(Math.round(sy));
2144
Grace Kloba3a0def22010-01-23 21:11:54 -08002145 // update webkit
2146 sendViewSizeZoom();
2147 sendOurVisibleRect();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002148 }
2149 }
2150 }
2151
2152 // Used to avoid sending many visible rect messages.
2153 private Rect mLastVisibleRectSent;
2154 private Rect mLastGlobalRect;
2155
2156 private Rect sendOurVisibleRect() {
Grace Kloba3a0def22010-01-23 21:11:54 -08002157 if (mPreviewZoomOnly) return mLastVisibleRectSent;
2158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002159 Rect rect = new Rect();
2160 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002161 // Rect.equals() checks for null input.
2162 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04002163 Point pos = new Point(rect.left, rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002164 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Cary Clarked56eda2009-06-18 09:48:47 -04002165 nativeMoveGeneration(), 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002166 mLastVisibleRectSent = rect;
2167 }
2168 Rect globalRect = new Rect();
2169 if (getGlobalVisibleRect(globalRect)
2170 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04002171 if (DebugFlags.WEB_VIEW) {
2172 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
2173 + globalRect.top + ",r=" + globalRect.right + ",b="
2174 + globalRect.bottom);
2175 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002176 // TODO: the global offset is only used by windowRect()
2177 // in ChromeClientAndroid ; other clients such as touch
2178 // and mouse events could return view + screen relative points.
2179 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
2180 mLastGlobalRect = globalRect;
2181 }
2182 return rect;
2183 }
2184
2185 // Sets r to be the visible rectangle of our webview in view coordinates
2186 private void calcOurVisibleRect(Rect r) {
2187 Point p = new Point();
2188 getGlobalVisibleRect(r, p);
2189 r.offset(-p.x, -p.y);
Cary Clark3524be92009-06-22 13:09:11 -04002190 if (mFindIsUp) {
Cary Clark5bb6b522009-09-21 11:58:31 -04002191 r.bottom -= mFindHeight;
Cary Clark3524be92009-06-22 13:09:11 -04002192 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002193 }
2194
2195 // Sets r to be our visible rectangle in content coordinates
2196 private void calcOurContentVisibleRect(Rect r) {
2197 calcOurVisibleRect(r);
Mike Reed180403a2010-03-10 11:34:26 -05002198 // since we might overscroll, pin the rect to the bounds of the content
2199 r.left = Math.max(viewToContentX(r.left), 0);
Leon Scroggins37df6a82009-09-23 10:31:23 -04002200 // viewToContentY will remove the total height of the title bar. Add
2201 // the visible height back in to account for the fact that if the title
2202 // bar is partially visible, the part of the visible rect which is
2203 // displaying our content is displaced by that amount.
Mike Reed180403a2010-03-10 11:34:26 -05002204 r.top = Math.max(viewToContentY(r.top + getVisibleTitleHeight()), 0);
2205 r.right = Math.min(viewToContentX(r.right), mContentWidth);
2206 r.bottom = Math.min(viewToContentY(r.bottom), mContentHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002207 }
2208
Grace Klobaef347ef2009-07-30 11:20:32 -07002209 static class ViewSizeData {
2210 int mWidth;
2211 int mHeight;
2212 int mTextWrapWidth;
Grace Kloba3a0def22010-01-23 21:11:54 -08002213 int mAnchorX;
2214 int mAnchorY;
Grace Klobaef347ef2009-07-30 11:20:32 -07002215 float mScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002216 boolean mIgnoreHeight;
Grace Klobaef347ef2009-07-30 11:20:32 -07002217 }
2218
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002219 /**
2220 * Compute unzoomed width and height, and if they differ from the last
2221 * values we sent, send them to webkit (to be used has new viewport)
2222 *
2223 * @return true if new values were sent
2224 */
2225 private boolean sendViewSizeZoom() {
Grace Kloba3a0def22010-01-23 21:11:54 -08002226 if (mPreviewZoomOnly) return false;
2227
Grace Klobaef347ef2009-07-30 11:20:32 -07002228 int viewWidth = getViewWidth();
2229 int newWidth = Math.round(viewWidth * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002230 int newHeight = Math.round(getViewHeight() * mInvActualScale);
2231 /*
2232 * Because the native side may have already done a layout before the
2233 * View system was able to measure us, we have to send a height of 0 to
2234 * remove excess whitespace when we grow our width. This will trigger a
2235 * layout and a change in content size. This content size change will
2236 * mean that contentSizeChanged will either call this method directly or
2237 * indirectly from onSizeChanged.
2238 */
2239 if (newWidth > mLastWidthSent && mWrapContent) {
2240 newHeight = 0;
2241 }
2242 // Avoid sending another message if the dimensions have not changed.
2243 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
Grace Klobaef347ef2009-07-30 11:20:32 -07002244 ViewSizeData data = new ViewSizeData();
2245 data.mWidth = newWidth;
2246 data.mHeight = newHeight;
Grace Kloba3a0def22010-01-23 21:11:54 -08002247 data.mTextWrapWidth = Math.round(viewWidth / mTextWrapScale);;
Grace Klobaef347ef2009-07-30 11:20:32 -07002248 data.mScale = mActualScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002249 data.mIgnoreHeight = mZoomScale != 0 && !mHeightCanMeasure;
Grace Kloba3a0def22010-01-23 21:11:54 -08002250 data.mAnchorX = mAnchorX;
2251 data.mAnchorY = mAnchorY;
Grace Klobaef347ef2009-07-30 11:20:32 -07002252 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002253 mLastWidthSent = newWidth;
2254 mLastHeightSent = newHeight;
Grace Kloba3a0def22010-01-23 21:11:54 -08002255 mAnchorX = mAnchorY = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002256 return true;
2257 }
2258 return false;
2259 }
2260
2261 @Override
2262 protected int computeHorizontalScrollRange() {
2263 if (mDrawHistory) {
2264 return mHistoryWidth;
2265 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002266 // to avoid rounding error caused unnecessary scrollbar, use floor
2267 return (int) Math.floor(mContentWidth * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002268 }
2269 }
2270
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002271 @Override
2272 protected int computeVerticalScrollRange() {
2273 if (mDrawHistory) {
2274 return mHistoryHeight;
2275 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002276 // to avoid rounding error caused unnecessary scrollbar, use floor
2277 return (int) Math.floor(mContentHeight * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002278 }
2279 }
2280
Leon Scroggins0236e672009-09-02 21:12:08 -04002281 @Override
2282 protected int computeVerticalScrollOffset() {
Mike Reede8853fc2009-09-04 14:01:48 -04002283 return Math.max(mScrollY - getTitleHeight(), 0);
Leon Scroggins0236e672009-09-02 21:12:08 -04002284 }
2285
2286 @Override
2287 protected int computeVerticalScrollExtent() {
2288 return getViewHeight();
2289 }
2290
Mike Reede8853fc2009-09-04 14:01:48 -04002291 /** @hide */
2292 @Override
2293 protected void onDrawVerticalScrollBar(Canvas canvas,
2294 Drawable scrollBar,
2295 int l, int t, int r, int b) {
2296 scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
2297 scrollBar.draw(canvas);
2298 }
2299
Grace Klobad7625dd2010-03-04 11:46:12 -08002300 @Override
2301 protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
2302 boolean clampedY) {
2303 mInOverScrollMode = false;
2304 int maxX = computeMaxScrollX();
Grace Klobaee8500e2010-03-19 17:23:38 -07002305 if (maxX == 0) {
2306 // do not over scroll x if the page just fits the screen
Grace Klobad7625dd2010-03-04 11:46:12 -08002307 scrollX = pinLocX(scrollX);
2308 } else if (scrollX < 0 || scrollX > maxX) {
2309 mInOverScrollMode = true;
2310 }
2311 if (scrollY < 0 || scrollY > computeMaxScrollY()) {
2312 mInOverScrollMode = true;
2313 }
2314 super.scrollTo(scrollX, scrollY);
2315 }
2316
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002317 /**
2318 * Get the url for the current page. This is not always the same as the url
2319 * passed to WebViewClient.onPageStarted because although the load for
2320 * that url has begun, the current page may not have changed.
2321 * @return The url for the current page.
2322 */
2323 public String getUrl() {
2324 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2325 return h != null ? h.getUrl() : null;
2326 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002327
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002328 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002329 * Get the original url for the current page. This is not always the same
2330 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002331 * load for that url has begun, the current page may not have changed.
2332 * Also, there may have been redirects resulting in a different url to that
2333 * originally requested.
2334 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002335 */
2336 public String getOriginalUrl() {
2337 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2338 return h != null ? h.getOriginalUrl() : null;
2339 }
2340
2341 /**
2342 * Get the title for the current page. This is the title of the current page
2343 * until WebViewClient.onReceivedTitle is called.
2344 * @return The title for the current page.
2345 */
2346 public String getTitle() {
2347 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2348 return h != null ? h.getTitle() : null;
2349 }
2350
2351 /**
2352 * Get the favicon for the current page. This is the favicon of the current
2353 * page until WebViewClient.onReceivedIcon is called.
2354 * @return The favicon for the current page.
2355 */
2356 public Bitmap getFavicon() {
2357 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2358 return h != null ? h.getFavicon() : null;
2359 }
2360
2361 /**
Patrick Scott2ba12622009-08-04 13:20:05 -04002362 * Get the touch icon url for the apple-touch-icon <link> element.
2363 * @hide
2364 */
2365 public String getTouchIconUrl() {
2366 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2367 return h != null ? h.getTouchIconUrl() : null;
2368 }
2369
2370 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002371 * Get the progress for the current page.
2372 * @return The progress for the current page between 0 and 100.
2373 */
2374 public int getProgress() {
2375 return mCallbackProxy.getProgress();
2376 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002377
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002378 /**
2379 * @return the height of the HTML content.
2380 */
2381 public int getContentHeight() {
2382 return mContentHeight;
2383 }
2384
2385 /**
Leon Scrogginsea96d1e2009-09-23 13:41:01 -04002386 * @return the width of the HTML content.
2387 * @hide
2388 */
2389 public int getContentWidth() {
2390 return mContentWidth;
2391 }
2392
2393 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002394 * Pause all layout, parsing, and javascript timers for all webviews. This
2395 * is a global requests, not restricted to just this webview. This can be
2396 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002397 */
2398 public void pauseTimers() {
2399 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
2400 }
2401
2402 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002403 * Resume all layout, parsing, and javascript timers for all webviews.
2404 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002405 */
2406 public void resumeTimers() {
2407 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
2408 }
2409
2410 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002411 * Call this to pause any extra processing associated with this view and
2412 * its associated DOM/plugins/javascript/etc. For example, if the view is
2413 * taken offscreen, this could be called to reduce unnecessary CPU and/or
2414 * network traffic. When the view is again "active", call onResume().
2415 *
2416 * Note that this differs from pauseTimers(), which affects all views/DOMs
2417 * @hide
2418 */
2419 public void onPause() {
2420 if (!mIsPaused) {
2421 mIsPaused = true;
2422 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
2423 }
2424 }
2425
2426 /**
2427 * Call this to balanace a previous call to onPause()
2428 * @hide
2429 */
2430 public void onResume() {
2431 if (mIsPaused) {
2432 mIsPaused = false;
2433 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2434 }
2435 }
2436
2437 /**
2438 * Returns true if the view is paused, meaning onPause() was called. Calling
2439 * onResume() sets the paused state back to false.
2440 * @hide
2441 */
2442 public boolean isPaused() {
2443 return mIsPaused;
2444 }
2445
2446 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002447 * Call this to inform the view that memory is low so that it can
2448 * free any available memory.
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002449 */
2450 public void freeMemory() {
2451 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2452 }
2453
2454 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002455 * Clear the resource cache. Note that the cache is per-application, so
2456 * this will clear the cache for all WebViews used.
2457 *
2458 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002459 */
2460 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002461 // Note: this really needs to be a static method as it clears cache for all
2462 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2463 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002464 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2465 includeDiskFiles ? 1 : 0, 0);
2466 }
2467
2468 /**
2469 * Make sure that clearing the form data removes the adapter from the
2470 * currently focused textfield if there is one.
2471 */
2472 public void clearFormData() {
2473 if (inEditingMode()) {
2474 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002475 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002476 }
2477 }
2478
2479 /**
2480 * Tell the WebView to clear its internal back/forward list.
2481 */
2482 public void clearHistory() {
2483 mCallbackProxy.getBackForwardList().setClearPending();
2484 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2485 }
2486
2487 /**
2488 * Clear the SSL preferences table stored in response to proceeding with SSL
2489 * certificate errors.
2490 */
2491 public void clearSslPreferences() {
2492 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2493 }
2494
2495 /**
2496 * Return the WebBackForwardList for this WebView. This contains the
2497 * back/forward list for use in querying each item in the history stack.
2498 * This is a copy of the private WebBackForwardList so it contains only a
2499 * snapshot of the current state. Multiple calls to this method may return
2500 * different objects. The object returned from this method will not be
2501 * updated to reflect any new state.
2502 */
2503 public WebBackForwardList copyBackForwardList() {
2504 return mCallbackProxy.getBackForwardList().clone();
2505 }
2506
2507 /*
2508 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04002509 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002510 * calling findAll.
2511 *
2512 * @param forward Direction to search.
2513 */
2514 public void findNext(boolean forward) {
Cary Clark7f970112009-10-15 15:29:08 -04002515 if (0 == mNativeClass) return; // client isn't initialized
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002516 nativeFindNext(forward);
2517 }
2518
2519 /*
2520 * Find all instances of find on the page and highlight them.
2521 * @param find String to find.
2522 * @return int The number of occurances of the String "find"
2523 * that were found.
2524 */
2525 public int findAll(String find) {
Cary Clark7f970112009-10-15 15:29:08 -04002526 if (0 == mNativeClass) return 0; // client isn't initialized
Cary Clarkde023c12010-03-03 10:05:16 -05002527 int result = find != null ? nativeFindAll(find.toLowerCase(),
2528 find.toUpperCase()) : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002529 invalidate();
Leon Scroggins5de63892009-10-29 09:48:43 -04002530 mLastFind = find;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002531 return result;
2532 }
2533
Cary Clark3403eb32010-03-03 10:05:16 -05002534 /**
Cary Clarkde023c12010-03-03 10:05:16 -05002535 * @hide
2536 */
2537 public void setFindIsUp(boolean isUp) {
2538 mFindIsUp = isUp;
2539 if (isUp) {
2540 recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
2541 false);
2542 }
2543 if (0 == mNativeClass) return; // client isn't initialized
2544 nativeSetFindIsUp(isUp);
2545 }
2546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002547 // Used to know whether the find dialog is open. Affects whether
2548 // or not we draw the highlights for matches.
2549 private boolean mFindIsUp;
Cary Clarkde023c12010-03-03 10:05:16 -05002550
Cary Clark5bb6b522009-09-21 11:58:31 -04002551 private int mFindHeight;
Leon Scroggins5de63892009-10-29 09:48:43 -04002552 // Keep track of the last string sent, so we can search again after an
2553 // orientation change or the dismissal of the soft keyboard.
2554 private String mLastFind;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002555
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002556 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002557 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002558 * location. Currently, only addresses in the United States are detected,
2559 * and consist of:
2560 * - a house number
2561 * - a street name
2562 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2563 * - a city name
2564 * - a state or territory, either spelled out or two-letter abbr.
2565 * - an optional 5 digit or 9 digit zip code.
2566 *
2567 * All names must be correctly capitalized, and the zip code, if present,
2568 * must be valid for the state. The street type must be a standard USPS
2569 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04002570 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002571 * five digits.
2572 * @param addr The string to search for addresses.
2573 *
2574 * @return the address, or if no address is found, return null.
2575 */
2576 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04002577 return findAddress(addr, false);
2578 }
2579
2580 /**
2581 * @hide
2582 * Return the first substring consisting of the address of a physical
2583 * location. Currently, only addresses in the United States are detected,
2584 * and consist of:
2585 * - a house number
2586 * - a street name
2587 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2588 * - a city name
2589 * - a state or territory, either spelled out or two-letter abbr.
2590 * - an optional 5 digit or 9 digit zip code.
2591 *
2592 * Names are optionally capitalized, and the zip code, if present,
2593 * must be valid for the state. The street type must be a standard USPS
2594 * spelling or abbreviation. The state or territory must also be spelled
2595 * or abbreviated using USPS standards. The house number may not exceed
2596 * five digits.
2597 * @param addr The string to search for addresses.
2598 * @param caseInsensitive addr Set to true to make search ignore case.
2599 *
2600 * @return the address, or if no address is found, return null.
2601 */
2602 public static String findAddress(String addr, boolean caseInsensitive) {
2603 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002604 }
2605
2606 /*
2607 * Clear the highlighting surrounding text matches created by findAll.
2608 */
2609 public void clearMatches() {
Cary Clarkde023c12010-03-03 10:05:16 -05002610 mLastFind = "";
Cary Clark32847a92009-11-17 16:04:18 -05002611 if (mNativeClass == 0)
2612 return;
Cary Clarkde023c12010-03-03 10:05:16 -05002613 nativeSetFindIsEmpty();
2614 invalidate();
2615 }
2616
2617 /**
2618 * @hide
2619 */
2620 public void notifyFindDialogDismissed() {
Shimeng (Simon) Wang4d8ef422010-03-22 17:06:17 -07002621 if (mWebViewCore == null) {
2622 return;
2623 }
Cary Clarkde023c12010-03-03 10:05:16 -05002624 clearMatches();
2625 setFindIsUp(false);
2626 recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
2627 false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002628 // Now that the dialog has been removed, ensure that we scroll to a
2629 // location that is not beyond the end of the page.
2630 pinScrollTo(mScrollX, mScrollY, false, 0);
2631 invalidate();
2632 }
2633
2634 /**
Cary Clark5bb6b522009-09-21 11:58:31 -04002635 * @hide
2636 */
2637 public void setFindDialogHeight(int height) {
2638 if (DebugFlags.WEB_VIEW) {
2639 Log.v(LOGTAG, "setFindDialogHeight height=" + height);
2640 }
2641 mFindHeight = height;
2642 }
2643
2644 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002645 * Query the document to see if it contains any image references. The
2646 * message object will be dispatched with arg1 being set to 1 if images
2647 * were found and 0 if the document does not reference any images.
2648 * @param response The message that will be dispatched with the result.
2649 */
2650 public void documentHasImages(Message response) {
2651 if (response == null) {
2652 return;
2653 }
2654 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
2655 }
2656
2657 @Override
2658 public void computeScroll() {
2659 if (mScroller.computeScrollOffset()) {
2660 int oldX = mScrollX;
2661 int oldY = mScrollY;
Grace Klobad7625dd2010-03-04 11:46:12 -08002662 int x = mScroller.getCurrX();
2663 int y = mScroller.getCurrY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002664 postInvalidate(); // So we draw again
Grace Klobad7625dd2010-03-04 11:46:12 -08002665 if (oldX != x || oldY != y) {
2666 overscrollBy(x - oldX, y - oldY, oldX, oldY,
2667 computeMaxScrollX(), computeMaxScrollY(),
Adam Powell8a836a82010-03-17 20:17:04 -07002668 getViewWidth() / 3, getViewHeight() / 3, false);
Grace Klobad7625dd2010-03-04 11:46:12 -08002669 onScrollChanged(mScrollX, mScrollY, oldX, oldY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002670 }
2671 } else {
2672 super.computeScroll();
2673 }
2674 }
2675
2676 private static int computeDuration(int dx, int dy) {
2677 int distance = Math.max(Math.abs(dx), Math.abs(dy));
2678 int duration = distance * 1000 / STD_SPEED;
2679 return Math.min(duration, MAX_DURATION);
2680 }
2681
2682 // helper to pin the scrollBy parameters (already in view coordinates)
2683 // returns true if the scroll was changed
2684 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
2685 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
2686 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002687 // helper to pin the scrollTo parameters (already in view coordinates)
2688 // returns true if the scroll was changed
2689 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
2690 x = pinLocX(x);
2691 y = pinLocY(y);
2692 int dx = x - mScrollX;
2693 int dy = y - mScrollY;
2694
2695 if ((dx | dy) == 0) {
2696 return false;
2697 }
Leon Scrogginsd55de402009-09-17 14:19:49 -04002698 if (animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002699 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002700 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
2701 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
Mike Cleronf116bf82009-09-27 19:14:12 -07002702 awakenScrollBars(mScroller.getDuration());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002703 invalidate();
2704 } else {
Cary Clark278ce052009-08-31 16:08:42 -04002705 abortAnimation(); // just in case
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002706 scrollTo(x, y);
2707 }
2708 return true;
2709 }
2710
2711 // Scale from content to view coordinates, and pin.
2712 // Also called by jni webview.cpp
Leon Scroggins4c943042009-07-31 15:05:42 -04002713 private boolean setContentScrollBy(int cx, int cy, boolean animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002714 if (mDrawHistory) {
2715 // disallow WebView to change the scroll position as History Picture
2716 // is used in the view system.
2717 // TODO: as we switchOutDrawHistory when trackball or navigation
2718 // keys are hit, this should be safe. Right?
Leon Scroggins4c943042009-07-31 15:05:42 -04002719 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002720 }
Mike Reede8853fc2009-09-04 14:01:48 -04002721 cx = contentToViewDimension(cx);
2722 cy = contentToViewDimension(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002723 if (mHeightCanMeasure) {
2724 // move our visible rect according to scroll request
2725 if (cy != 0) {
2726 Rect tempRect = new Rect();
2727 calcOurVisibleRect(tempRect);
2728 tempRect.offset(cx, cy);
2729 requestRectangleOnScreen(tempRect);
2730 }
2731 // FIXME: We scroll horizontally no matter what because currently
2732 // ScrollView and ListView will not scroll horizontally.
2733 // FIXME: Why do we only scroll horizontally if there is no
2734 // vertical scroll?
2735// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
Leon Scroggins4c943042009-07-31 15:05:42 -04002736 return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002737 } else {
Leon Scroggins4c943042009-07-31 15:05:42 -04002738 return pinScrollBy(cx, cy, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002739 }
2740 }
2741
Leon Scroggins405d7852009-11-02 14:50:54 -08002742 /**
2743 * Called by CallbackProxy when the page finishes loading.
2744 * @param url The URL of the page which has finished loading.
2745 */
2746 /* package */ void onPageFinished(String url) {
2747 if (mPageThatNeedsToSlideTitleBarOffScreen != null) {
2748 // If the user is now on a different page, or has scrolled the page
2749 // past the point where the title bar is offscreen, ignore the
2750 // scroll request.
2751 if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url)
2752 && mScrollX == 0 && mScrollY == 0) {
2753 pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true,
2754 SLIDE_TITLE_DURATION);
2755 }
2756 mPageThatNeedsToSlideTitleBarOffScreen = null;
2757 }
2758 }
2759
2760 /**
2761 * The URL of a page that sent a message to scroll the title bar off screen.
2762 *
2763 * Many mobile sites tell the page to scroll to (0,1) in order to scroll the
2764 * title bar off the screen. Sometimes, the scroll position is set before
2765 * the page finishes loading. Rather than scrolling while the page is still
2766 * loading, keep track of the URL and new scroll position so we can perform
2767 * the scroll once the page finishes loading.
2768 */
2769 private String mPageThatNeedsToSlideTitleBarOffScreen;
2770
2771 /**
2772 * The destination Y scroll position to be used when the page finishes
2773 * loading. See mPageThatNeedsToSlideTitleBarOffScreen.
2774 */
2775 private int mYDistanceToSlideTitleOffScreen;
2776
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002777 // scale from content to view coordinates, and pin
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002778 // return true if pin caused the final x/y different than the request cx/cy,
2779 // and a future scroll may reach the request cx/cy after our size has
2780 // changed
2781 // return false if the view scroll to the exact position as it is requested,
2782 // where negative numbers are taken to mean 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002783 private boolean setContentScrollTo(int cx, int cy) {
2784 if (mDrawHistory) {
2785 // disallow WebView to change the scroll position as History Picture
2786 // is used in the view system.
2787 // One known case where this is called is that WebCore tries to
2788 // restore the scroll position. As history Picture already uses the
2789 // saved scroll position, it is ok to skip this.
2790 return false;
2791 }
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002792 int vx;
2793 int vy;
2794 if ((cx | cy) == 0) {
2795 // If the page is being scrolled to (0,0), do not add in the title
2796 // bar's height, and simply scroll to (0,0). (The only other work
2797 // in contentToView_ is to multiply, so this would not change 0.)
2798 vx = 0;
2799 vy = 0;
2800 } else {
2801 vx = contentToViewX(cx);
2802 vy = contentToViewY(cy);
2803 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002804// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
2805// vx + " " + vy + "]");
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002806 // Some mobile sites attempt to scroll the title bar off the page by
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002807 // scrolling to (0,1). If we are at the top left corner of the
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002808 // page, assume this is an attempt to scroll off the title bar, and
2809 // animate the title bar off screen slowly enough that the user can see
2810 // it.
Leon Scroggins405d7852009-11-02 14:50:54 -08002811 if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0
2812 && mTitleBar != null) {
2813 // FIXME: 100 should be defined somewhere as our max progress.
2814 if (getProgress() < 100) {
2815 // Wait to scroll the title bar off screen until the page has
2816 // finished loading. Keep track of the URL and the destination
2817 // Y position
2818 mPageThatNeedsToSlideTitleBarOffScreen = getUrl();
2819 mYDistanceToSlideTitleOffScreen = vy;
2820 } else {
2821 pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
2822 }
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002823 // Since we are animating, we have not yet reached the desired
2824 // scroll position. Do not return true to request another attempt
2825 return false;
2826 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002827 pinScrollTo(vx, vy, false, 0);
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002828 // If the request was to scroll to a negative coordinate, treat it as if
2829 // it was a request to scroll to 0
2830 if ((mScrollX != vx && cx >= 0) || (mScrollY != vy && cy >= 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002831 return true;
2832 } else {
2833 return false;
2834 }
2835 }
2836
2837 // scale from content to view coordinates, and pin
2838 private void spawnContentScrollTo(int cx, int cy) {
2839 if (mDrawHistory) {
2840 // disallow WebView to change the scroll position as History Picture
2841 // is used in the view system.
2842 return;
2843 }
Leon Scroggins0236e672009-09-02 21:12:08 -04002844 int vx = contentToViewX(cx);
2845 int vy = contentToViewY(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002846 pinScrollTo(vx, vy, true, 0);
2847 }
2848
2849 /**
2850 * These are from webkit, and are in content coordinate system (unzoomed)
2851 */
2852 private void contentSizeChanged(boolean updateLayout) {
2853 // suppress 0,0 since we usually see real dimensions soon after
2854 // this avoids drawing the prev content in a funny place. If we find a
2855 // way to consolidate these notifications, this check may become
2856 // obsolete
2857 if ((mContentWidth | mContentHeight) == 0) {
2858 return;
2859 }
2860
2861 if (mHeightCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002862 if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002863 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002864 requestLayout();
2865 }
2866 } else if (mWidthCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002867 if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002868 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002869 requestLayout();
2870 }
2871 } else {
2872 // If we don't request a layout, try to send our view size to the
2873 // native side to ensure that WebCore has the correct dimensions.
2874 sendViewSizeZoom();
2875 }
2876 }
2877
2878 /**
2879 * Set the WebViewClient that will receive various notifications and
2880 * requests. This will replace the current handler.
2881 * @param client An implementation of WebViewClient.
2882 */
2883 public void setWebViewClient(WebViewClient client) {
2884 mCallbackProxy.setWebViewClient(client);
2885 }
2886
2887 /**
Grace Kloba94ab3b62009-10-07 18:00:19 -07002888 * Gets the WebViewClient
2889 * @return the current WebViewClient instance.
2890 *
2891 *@hide pending API council approval.
2892 */
2893 public WebViewClient getWebViewClient() {
2894 return mCallbackProxy.getWebViewClient();
2895 }
2896
2897 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002898 * Register the interface to be used when content can not be handled by
2899 * the rendering engine, and should be downloaded instead. This will replace
2900 * the current handler.
2901 * @param listener An implementation of DownloadListener.
2902 */
2903 public void setDownloadListener(DownloadListener listener) {
2904 mCallbackProxy.setDownloadListener(listener);
2905 }
2906
2907 /**
2908 * Set the chrome handler. This is an implementation of WebChromeClient for
2909 * use in handling Javascript dialogs, favicons, titles, and the progress.
2910 * This will replace the current handler.
2911 * @param client An implementation of WebChromeClient.
2912 */
2913 public void setWebChromeClient(WebChromeClient client) {
2914 mCallbackProxy.setWebChromeClient(client);
2915 }
2916
2917 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01002918 * Gets the chrome handler.
2919 * @return the current WebChromeClient instance.
2920 *
2921 * @hide API council approval.
2922 */
2923 public WebChromeClient getWebChromeClient() {
2924 return mCallbackProxy.getWebChromeClient();
2925 }
2926
2927 /**
Patrick Scott0b2e84b2010-03-02 08:58:44 -05002928 * Set the back/forward list client. This is an implementation of
2929 * WebBackForwardListClient for handling new items and changes in the
2930 * history index.
2931 * @param client An implementation of WebBackForwardListClient.
2932 * {@hide}
2933 */
2934 public void setWebBackForwardListClient(WebBackForwardListClient client) {
2935 mCallbackProxy.setWebBackForwardListClient(client);
2936 }
2937
2938 /**
2939 * Gets the WebBackForwardListClient.
2940 * {@hide}
2941 */
2942 public WebBackForwardListClient getWebBackForwardListClient() {
2943 return mCallbackProxy.getWebBackForwardListClient();
2944 }
2945
2946 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002947 * Set the Picture listener. This is an interface used to receive
2948 * notifications of a new Picture.
2949 * @param listener An implementation of WebView.PictureListener.
2950 */
2951 public void setPictureListener(PictureListener listener) {
2952 mPictureListener = listener;
2953 }
2954
2955 /**
2956 * {@hide}
2957 */
2958 /* FIXME: Debug only! Remove for SDK! */
2959 public void externalRepresentation(Message callback) {
2960 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
2961 }
2962
2963 /**
2964 * {@hide}
2965 */
2966 /* FIXME: Debug only! Remove for SDK! */
2967 public void documentAsText(Message callback) {
2968 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
2969 }
2970
2971 /**
2972 * Use this function to bind an object to Javascript so that the
2973 * methods can be accessed from Javascript.
2974 * <p><strong>IMPORTANT:</strong>
2975 * <ul>
2976 * <li> Using addJavascriptInterface() allows JavaScript to control your
2977 * application. This can be a very useful feature or a dangerous security
2978 * issue. When the HTML in the WebView is untrustworthy (for example, part
2979 * or all of the HTML is provided by some person or process), then an
2980 * attacker could inject HTML that will execute your code and possibly any
2981 * code of the attacker's choosing.<br>
2982 * Do not use addJavascriptInterface() unless all of the HTML in this
2983 * WebView was written by you.</li>
2984 * <li> The Java object that is bound runs in another thread and not in
2985 * the thread that it was constructed in.</li>
2986 * </ul></p>
2987 * @param obj The class instance to bind to Javascript
2988 * @param interfaceName The name to used to expose the class in Javascript
2989 */
2990 public void addJavascriptInterface(Object obj, String interfaceName) {
Cary Clarkded054c2009-06-15 10:26:08 -04002991 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
2992 arg.mObject = obj;
2993 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002994 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
2995 }
2996
2997 /**
2998 * Return the WebSettings object used to control the settings for this
2999 * WebView.
3000 * @return A WebSettings object that can be used to control this WebView's
3001 * settings.
3002 */
3003 public WebSettings getSettings() {
3004 return mWebViewCore.getSettings();
3005 }
3006
Andrei Popescua6d747d2010-02-11 13:19:21 +00003007 /**
3008 * Use this method to inform the webview about packages that are installed
3009 * in the system. This information will be used by the
3010 * navigator.isApplicationInstalled() API.
3011 * @param packageNames is a set of package names that are known to be
3012 * installed in the system.
3013 *
3014 * @hide not a public API
3015 */
3016 public void addPackageNames(Set<String> packageNames) {
3017 mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, packageNames);
3018 }
3019
3020 /**
3021 * Use this method to inform the webview about single packages that are
3022 * installed in the system. This information will be used by the
3023 * navigator.isApplicationInstalled() API.
3024 * @param packageName is the name of a package that is known to be
3025 * installed in the system.
3026 *
3027 * @hide not a public API
3028 */
3029 public void addPackageName(String packageName) {
3030 mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAME, packageName);
3031 }
3032
3033 /**
3034 * Use this method to inform the webview about packages that are uninstalled
3035 * in the system. This information will be used by the
3036 * navigator.isApplicationInstalled() API.
3037 * @param packageName is the name of a package that has been uninstalled in
3038 * the system.
3039 *
3040 * @hide not a public API
3041 */
3042 public void removePackageName(String packageName) {
3043 mWebViewCore.sendMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
3044 }
3045
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003046 /**
3047 * Return the list of currently loaded plugins.
3048 * @return The list of currently loaded plugins.
Andrei Popescu385df692009-08-13 11:59:57 +01003049 *
3050 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003051 */
Andrei Popescu385df692009-08-13 11:59:57 +01003052 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003053 public static synchronized PluginList getPluginList() {
Grace Klobabb245ea2009-11-10 13:13:24 -08003054 return new PluginList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003055 }
3056
3057 /**
Andrei Popescu385df692009-08-13 11:59:57 +01003058 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003059 */
Andrei Popescu385df692009-08-13 11:59:57 +01003060 @Deprecated
3061 public void refreshPlugins(boolean reloadOpenPages) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003062
3063 //-------------------------------------------------------------------------
3064 // Override View methods
3065 //-------------------------------------------------------------------------
3066
3067 @Override
3068 protected void finalize() throws Throwable {
Cary Clark9a4c0632009-08-10 16:40:08 -04003069 try {
3070 destroy();
3071 } finally {
3072 super.finalize();
3073 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003074 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003075
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003076 @Override
Leon Scroggins0236e672009-09-02 21:12:08 -04003077 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
3078 if (child == mTitleBar) {
3079 // When drawing the title bar, move it horizontally to always show
Grace Klobad7625dd2010-03-04 11:46:12 -08003080 // at the top of the WebView. While overscroll, stick the title bar
3081 // on the top otherwise we may have two during loading, one is drawn
3082 // here, another is drawn by the Browser.
Leon Scroggins0236e672009-09-02 21:12:08 -04003083 mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
Grace Klobad7625dd2010-03-04 11:46:12 -08003084 if (mScrollY <= 0) {
3085 mTitleBar.offsetTopAndBottom(mScrollY - mTitleBar.getTop());
3086 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003087 }
3088 return super.drawChild(canvas, child, drawingTime);
3089 }
3090
Mike Reed19f3f0e2009-11-12 12:50:20 -05003091 private void drawContent(Canvas canvas) {
Grace Kloba04b28682009-09-14 14:38:37 -07003092 // Update the buttons in the picture, so when we draw the picture
3093 // to the screen, they are in the correct state.
3094 // Tell the native side if user is a) touching the screen,
3095 // b) pressing the trackball down, or c) pressing the enter key
3096 // If the cursor is on a button, we need to draw it in the pressed
3097 // state.
3098 // If mNativeClass is 0, we should not reach here, so we do not
3099 // need to check it again.
3100 nativeRecordButtons(hasFocus() && hasWindowFocus(),
Mike Reed19f3f0e2009-11-12 12:50:20 -05003101 mTouchMode == TOUCH_SHORTPRESS_START_MODE
3102 || mTrackballDown || mGotCenterDown, false);
Grace Kloba04b28682009-09-14 14:38:37 -07003103 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
Mike Reed19f3f0e2009-11-12 12:50:20 -05003104 }
3105
3106 @Override
3107 protected void onDraw(Canvas canvas) {
3108 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
3109 if (mNativeClass == 0) {
3110 return;
3111 }
3112
Grace Klobae47ac472010-03-11 14:53:07 -08003113 // if both mContentWidth and mContentHeight are 0, it means there is no
3114 // valid Picture passed to WebView yet. This can happen when WebView
3115 // just starts. Draw the background and return.
3116 if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) {
3117 canvas.drawColor(mBackgroundColor);
3118 return;
3119 }
3120
Mike Reed19f3f0e2009-11-12 12:50:20 -05003121 int saveCount = canvas.save();
Grace Klobaf4418b02010-03-19 15:22:18 -07003122 if (mInOverScrollMode && !getSettings()
3123 .getUseWebViewBackgroundForOverscrollBackground()) {
Grace Klobad7625dd2010-03-04 11:46:12 -08003124 if (mOverScrollBackground == null) {
3125 mOverScrollBackground = new Paint();
3126 Bitmap bm = BitmapFactory.decodeResource(
3127 mContext.getResources(),
Grace Klobaf8338d42010-03-18 22:47:34 -07003128 com.android.internal.R.drawable.status_bar_background);
Grace Klobad7625dd2010-03-04 11:46:12 -08003129 mOverScrollBackground.setShader(new BitmapShader(bm,
3130 Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
3131 }
3132 int top = getTitleHeight();
3133 // first draw the background and anchor to the top of the view
3134 canvas.save();
3135 canvas.translate(mScrollX, mScrollY);
3136 canvas.clipRect(-mScrollX, top - mScrollY,
3137 computeHorizontalScrollRange() - mScrollX, top
3138 + computeVerticalScrollRange() - mScrollY,
3139 Region.Op.DIFFERENCE);
3140 canvas.drawPaint(mOverScrollBackground);
3141 canvas.restore();
3142 // next clip the region for the content
3143 canvas.clipRect(0, top, computeHorizontalScrollRange(), top
3144 + computeVerticalScrollRange());
3145 }
Mike Reed19f3f0e2009-11-12 12:50:20 -05003146 if (mTitleBar != null) {
3147 canvas.translate(0, (int) mTitleBar.getHeight());
3148 }
Mike Reedc95326092010-02-08 16:49:58 -05003149 if (mDragTrackerHandler == null) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05003150 drawContent(canvas);
Mike Reedc95326092010-02-08 16:49:58 -05003151 } else {
3152 if (!mDragTrackerHandler.draw(canvas)) {
3153 // sometimes the tracker doesn't draw, even though its active
3154 drawContent(canvas);
3155 }
3156 if (mDragTrackerHandler.isFinished()) {
3157 mDragTrackerHandler = null;
3158 }
Mike Reed19f3f0e2009-11-12 12:50:20 -05003159 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003160 canvas.restoreToCount(saveCount);
Cary Clarkd6982c92009-05-29 11:02:22 -04003161
Leon Scroggins58992ea2009-09-17 15:55:31 -04003162 // Now draw the shadow.
Grace Klobad7625dd2010-03-04 11:46:12 -08003163 int titleH = getVisibleTitleHeight();
3164 if (mTitleBar != null && titleH == 0) {
Leon Scroggins58992ea2009-09-17 15:55:31 -04003165 int height = (int) (5f * getContext().getResources()
3166 .getDisplayMetrics().density);
Grace Klobad7625dd2010-03-04 11:46:12 -08003167 mTitleShadow.setBounds(mScrollX, mScrollY, mScrollX + getWidth(),
3168 mScrollY + height);
Leon Scroggins58992ea2009-09-17 15:55:31 -04003169 mTitleShadow.draw(canvas);
3170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003171 if (AUTO_REDRAW_HACK && mAutoRedraw) {
3172 invalidate();
3173 }
Nicolas Roard38863332010-01-04 19:30:55 +00003174 mWebViewCore.signalRepaintDone();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003175 }
3176
3177 @Override
3178 public void setLayoutParams(ViewGroup.LayoutParams params) {
3179 if (params.height == LayoutParams.WRAP_CONTENT) {
3180 mWrapContent = true;
3181 }
3182 super.setLayoutParams(params);
3183 }
3184
3185 @Override
3186 public boolean performLongClick() {
Grace Kloba98e6fcf2010-01-27 15:20:30 -08003187 // performLongClick() is the result of a delayed message. If we switch
3188 // to windows overview, the WebView will be temporarily removed from the
3189 // view system. In that case, do nothing.
3190 if (getParent() == null) return false;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003191 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
3192 // Send the click so that the textfield is in focus
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003193 centerKeyPressOnTextField();
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003194 rebuildWebTextView();
3195 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003196 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003197 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003198 } else {
3199 return super.performLongClick();
3200 }
3201 }
3202
Grace Kloba94c715d2009-09-28 23:23:53 -07003203 boolean inAnimateZoom() {
3204 return mZoomScale != 0;
3205 }
3206
Leon Scroggins608f9f42009-09-03 10:06:04 -04003207 /**
3208 * Need to adjust the WebTextView after a change in zoom, since mActualScale
3209 * has changed. This is especially important for password fields, which are
3210 * drawn by the WebTextView, since it conveys more information than what
3211 * webkit draws. Thus we need to reposition it to show in the correct
3212 * place.
3213 */
3214 private boolean mNeedToAdjustWebTextView;
3215
Cary Clark5da9aeb2009-10-06 17:40:53 -04003216 private boolean didUpdateTextViewBounds(boolean allowIntersect) {
3217 Rect contentBounds = nativeFocusCandidateNodeBounds();
3218 Rect vBox = contentToViewRect(contentBounds);
3219 Rect visibleRect = new Rect();
3220 calcOurVisibleRect(visibleRect);
Leon Scrogginsbcbf5642010-02-17 17:56:51 -05003221 // If the textfield is on screen, place the WebTextView in
3222 // its new place, accounting for our new scroll/zoom values,
3223 // and adjust its textsize.
3224 if (allowIntersect ? Rect.intersects(visibleRect, vBox)
3225 : visibleRect.contains(vBox)) {
Cary Clark5da9aeb2009-10-06 17:40:53 -04003226 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
3227 vBox.height());
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003228 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
3229 contentToViewDimension(
3230 nativeFocusCandidateTextSize()));
Cary Clark5da9aeb2009-10-06 17:40:53 -04003231 return true;
3232 } else {
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003233 // The textfield is now off screen. The user probably
3234 // was not zooming to see the textfield better. Remove
3235 // the WebTextView. If the user types a key, and the
3236 // textfield is still in focus, we will reconstruct
3237 // the WebTextView and scroll it back on screen.
3238 mWebTextView.remove();
Cary Clark5da9aeb2009-10-06 17:40:53 -04003239 return false;
3240 }
3241 }
3242
Cary Clark2ec30692010-02-23 10:50:38 -05003243 private void drawExtras(Canvas canvas, int extras) {
Cary Clark018ff892010-02-25 14:49:26 -05003244 // If mNativeClass is 0, we should not reach here, so we do not
3245 // need to check it again.
Cary Clark2ec30692010-02-23 10:50:38 -05003246 // Currently for each draw we compute the animation values;
3247 // We may in the future decide to do that independently.
3248 if (nativeEvaluateLayersAnimations()) {
3249 // If we have unfinished (or unstarted) animations,
3250 // we ask for a repaint.
3251 invalidate();
Nicolas Roard38863332010-01-04 19:30:55 +00003252 }
Cary Clark2ec30692010-02-23 10:50:38 -05003253
3254 nativeDrawExtras(canvas, extras);
Nicolas Roard38863332010-01-04 19:30:55 +00003255 }
3256
Cary Clarkd6982c92009-05-29 11:02:22 -04003257 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003258 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003259 if (mDrawHistory) {
3260 canvas.scale(mActualScale, mActualScale);
3261 canvas.drawPicture(mHistoryPicture);
3262 return;
3263 }
3264
3265 boolean animateZoom = mZoomScale != 0;
Grace Klobac2242f22010-03-05 14:00:26 -08003266 boolean animateScroll = ((!mScroller.isFinished()
Cary Clark25415e22009-10-12 13:41:28 -04003267 || mVelocityTracker != null)
3268 && (mTouchMode != TOUCH_DRAG_MODE ||
Grace Klobac2242f22010-03-05 14:00:26 -08003269 mHeldMotionless != MOTIONLESS_TRUE))
3270 || mDeferTouchMode == TOUCH_DRAG_MODE;
Cary Clark25415e22009-10-12 13:41:28 -04003271 if (mTouchMode == TOUCH_DRAG_MODE) {
3272 if (mHeldMotionless == MOTIONLESS_PENDING) {
3273 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
3274 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
3275 mHeldMotionless = MOTIONLESS_FALSE;
3276 }
3277 if (mHeldMotionless == MOTIONLESS_FALSE) {
3278 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3279 .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
3280 mHeldMotionless = MOTIONLESS_PENDING;
3281 }
3282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003283 if (animateZoom) {
3284 float zoomScale;
3285 int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
3286 if (interval < ZOOM_ANIMATION_LENGTH) {
3287 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
Cary Clarkd6982c92009-05-29 11:02:22 -04003288 zoomScale = 1.0f / (mInvInitialZoomScale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003289 + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
3290 invalidate();
3291 } else {
3292 zoomScale = mZoomScale;
3293 // set mZoomScale to be 0 as we have done animation
3294 mZoomScale = 0;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08003295 WebViewCore.resumeUpdatePicture(mWebViewCore);
Grace Klobadfe095a2009-09-17 07:56:48 -07003296 // call invalidate() again to draw with the final filters
3297 invalidate();
Leon Scroggins608f9f42009-09-03 10:06:04 -04003298 if (mNeedToAdjustWebTextView) {
3299 mNeedToAdjustWebTextView = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003300 if (didUpdateTextViewBounds(false)
3301 && nativeFocusCandidateIsPassword()) {
Leon Scroggins10be7542009-09-30 18:01:38 -04003302 // If it is a password field, start drawing the
3303 // WebTextView once again.
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003304 mWebTextView.setInPassword(true);
Leon Scroggins608f9f42009-09-03 10:06:04 -04003305 }
3306 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003307 }
Grace Klobac0c03af2009-09-17 11:01:44 -07003308 // calculate the intermediate scroll position. As we need to use
3309 // zoomScale, we can't use pinLocX/Y directly. Copy the logic here.
Grace Kloba675c7d22009-07-23 09:21:21 -07003310 float scale = zoomScale * mInvInitialZoomScale;
3311 int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
3312 - mZoomCenterX);
3313 tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
3314 * zoomScale)) + mScrollX;
Grace Klobac0c03af2009-09-17 11:01:44 -07003315 int titleHeight = getTitleHeight();
3316 int ty = Math.round(scale
3317 * (mInitialScrollY + mZoomCenterY - titleHeight)
3318 - (mZoomCenterY - titleHeight));
3319 ty = -(ty <= titleHeight ? Math.max(ty, 0) : pinLoc(ty
3320 - titleHeight, getViewHeight(), Math.round(mContentHeight
3321 * zoomScale)) + titleHeight) + mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003322 canvas.translate(tx, ty);
3323 canvas.scale(zoomScale, zoomScale);
Leon Scroggins608f9f42009-09-03 10:06:04 -04003324 if (inEditingMode() && !mNeedToAdjustWebTextView
3325 && mZoomScale != 0) {
3326 // The WebTextView is up. Keep track of this so we can adjust
3327 // its size and placement when we finish zooming
3328 mNeedToAdjustWebTextView = true;
3329 // If it is in password mode, turn it off so it does not draw
3330 // misplaced.
3331 if (nativeFocusCandidateIsPassword()) {
3332 mWebTextView.setInPassword(false);
3333 }
3334 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003335 } else {
3336 canvas.scale(mActualScale, mActualScale);
3337 }
3338
Grace Kloba3a0def22010-01-23 21:11:54 -08003339 mWebViewCore.drawContentPicture(canvas, color,
3340 (animateZoom || mPreviewZoomOnly), animateScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003341 if (mNativeClass == 0) return;
Cary Clark2ec30692010-02-23 10:50:38 -05003342 // decide which adornments to draw
3343 int extras = DRAW_EXTRAS_NONE;
3344 if (mFindIsUp) {
3345 // When the FindDialog is up, only draw the matches if we are not in
3346 // the process of scrolling them into view.
3347 if (!animateScroll) {
3348 extras = DRAW_EXTRAS_FIND;
Cary Clark09e383c2009-10-26 16:43:58 -04003349 }
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003350 } else if (mShiftIsPressed && !nativeFocusIsPlugin()) {
Cary Clark2ec30692010-02-23 10:50:38 -05003351 if (!animateZoom && !mPreviewZoomOnly) {
3352 extras = DRAW_EXTRAS_SELECTION;
3353 nativeSetSelectionRegion(mTouchSelection || mExtendSelection);
3354 nativeSetSelectionPointer(!mTouchSelection, mInvActualScale,
3355 mSelectX, mSelectY - getTitleHeight(),
3356 mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003357 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003358 } else if (drawCursorRing) {
Cary Clark2ec30692010-02-23 10:50:38 -05003359 extras = DRAW_EXTRAS_CURSOR_RING;
3360 }
3361 drawExtras(canvas, extras);
3362
3363 if (extras == DRAW_EXTRAS_CURSOR_RING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003364 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3365 mTouchMode = TOUCH_SHORTPRESS_MODE;
3366 HitTestResult hitTest = getHitTestResult();
Grace Klobac2242f22010-03-05 14:00:26 -08003367 if (hitTest == null
3368 || hitTest.mType == HitTestResult.UNKNOWN_TYPE) {
3369 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003370 }
3371 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003372 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04003373 if (mFocusSizeChanged) {
3374 mFocusSizeChanged = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003375 // If we are zooming, this will get handled above, when the zoom
3376 // finishes. We also do not need to do this unless the WebTextView
3377 // is showing.
3378 if (!animateZoom && inEditingMode()) {
3379 didUpdateTextViewBounds(true);
3380 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04003381 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003382 }
3383
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003384 // draw history
3385 private boolean mDrawHistory = false;
3386 private Picture mHistoryPicture = null;
3387 private int mHistoryWidth = 0;
3388 private int mHistoryHeight = 0;
3389
3390 // Only check the flag, can be called from WebCore thread
3391 boolean drawHistory() {
3392 return mDrawHistory;
3393 }
3394
3395 // Should only be called in UI thread
3396 void switchOutDrawHistory() {
3397 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Cary Clarkbc2e33b2009-04-23 13:12:19 -04003398 if (mDrawHistory && mWebViewCore.pictureReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003399 mDrawHistory = false;
3400 invalidate();
3401 int oldScrollX = mScrollX;
3402 int oldScrollY = mScrollY;
3403 mScrollX = pinLocX(mScrollX);
3404 mScrollY = pinLocY(mScrollY);
3405 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
3406 mUserScroll = false;
3407 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
3408 oldScrollY);
3409 }
3410 sendOurVisibleRect();
3411 }
3412 }
3413
Cary Clarkd6982c92009-05-29 11:02:22 -04003414 WebViewCore.CursorData cursorData() {
3415 WebViewCore.CursorData result = new WebViewCore.CursorData();
3416 result.mMoveGeneration = nativeMoveGeneration();
3417 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04003418 Point position = nativeCursorPosition();
3419 result.mX = position.x;
3420 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04003421 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003422 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003424 /**
3425 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04003426 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003427 * order, swap them.
3428 * @param start Beginning of selection to delete.
3429 * @param end End of selection to delete.
3430 */
3431 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003432 mTextGeneration++;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04003433 WebViewCore.TextSelectionData data
3434 = new WebViewCore.TextSelectionData(start, end);
3435 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
3436 data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003437 }
3438
3439 /**
3440 * Set the selection to (start, end) in the focused textfield. If start and
3441 * end are out of order, swap them.
3442 * @param start Beginning of selection.
3443 * @param end End of selection.
3444 */
3445 /* package */ void setSelection(int start, int end) {
Cary Clark2f1d60c2009-06-03 08:05:53 -04003446 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003447 }
3448
Derek Sollenberger7cabb032010-01-21 10:37:38 -05003449 @Override
3450 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
3451 InputConnection connection = super.onCreateInputConnection(outAttrs);
3452 outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN;
3453 return connection;
3454 }
3455
Leon Scroggins04e0a102010-01-08 16:19:27 -05003456 /**
3457 * Called in response to a message from webkit telling us that the soft
3458 * keyboard should be launched.
3459 */
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003460 private void displaySoftKeyboard(boolean isTextView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003461 InputMethodManager imm = (InputMethodManager)
3462 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003463
Leon Scrogginse3844ee2010-02-23 15:15:16 -05003464 // bring it back to the default scale so that user can enter text
3465 boolean zoom = mActualScale < mDefaultScale;
3466 if (zoom) {
3467 mInZoomOverview = false;
3468 mZoomCenterX = mLastTouchX;
3469 mZoomCenterY = mLastTouchY;
3470 // do not change text wrap scale so that there is no reflow
3471 setNewZoomScale(mDefaultScale, false, false);
3472 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003473 if (isTextView) {
Leon Scroggins04e0a102010-01-08 16:19:27 -05003474 rebuildWebTextView();
Leon Scrogginse3844ee2010-02-23 15:15:16 -05003475 if (inEditingMode()) {
3476 imm.showSoftInput(mWebTextView, 0);
3477 if (zoom) {
3478 didUpdateTextViewBounds(true);
3479 }
3480 return;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003481 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003482 }
Leon Scrogginse3844ee2010-02-23 15:15:16 -05003483 // Used by plugins.
3484 // Also used if the navigation cache is out of date, and
3485 // does not recognize that a textfield is in focus. In that
3486 // case, use WebView as the targeted view.
3487 // see http://b/issue?id=2457459
3488 imm.showSoftInput(this, 0);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003489 }
3490
3491 // Called by WebKit to instruct the UI to hide the keyboard
3492 private void hideSoftKeyboard() {
3493 InputMethodManager imm = (InputMethodManager)
3494 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
3495
3496 imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003497 }
3498
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003499 /*
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003500 * This method checks the current focus and cursor and potentially rebuilds
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003501 * mWebTextView to have the appropriate properties, such as password,
3502 * multiline, and what text it contains. It also removes it if necessary.
3503 */
Leon Scroggins01058282009-07-30 16:33:56 -04003504 /* package */ void rebuildWebTextView() {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003505 // If the WebView does not have focus, do nothing until it gains focus.
Grace Kloba04b28682009-09-14 14:38:37 -07003506 if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003507 return;
3508 }
3509 boolean alreadyThere = inEditingMode();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003510 // inEditingMode can only return true if mWebTextView is non-null,
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003511 // so we can safely call remove() if (alreadyThere)
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003512 if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003513 if (alreadyThere) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003514 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003515 }
3516 return;
3517 }
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003518 // At this point, we know we have found an input field, so go ahead
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003519 // and create the WebTextView if necessary.
3520 if (mWebTextView == null) {
3521 mWebTextView = new WebTextView(mContext, WebView.this);
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003522 // Initialize our generation number.
3523 mTextGeneration = 0;
3524 }
Leon Scroggins3a6c88c2009-09-09 14:50:23 -04003525 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
3526 contentToViewDimension(nativeFocusCandidateTextSize()));
Cary Clark3524be92009-06-22 13:09:11 -04003527 Rect visibleRect = new Rect();
3528 calcOurContentVisibleRect(visibleRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003529 // Note that sendOurVisibleRect calls viewToContent, so the coordinates
3530 // should be in content coordinates.
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003531 Rect bounds = nativeFocusCandidateNodeBounds();
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003532 Rect vBox = contentToViewRect(bounds);
3533 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height());
Cary Clarkd6982c92009-05-29 11:02:22 -04003534 if (!Rect.intersects(bounds, visibleRect)) {
Leon Scroggins40981262009-07-01 10:57:47 -04003535 mWebTextView.bringIntoView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003536 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003537 String text = nativeFocusCandidateText();
3538 int nodePointer = nativeFocusCandidatePointer();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003539 if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003540 // It is possible that we have the same textfield, but it has moved,
3541 // i.e. In the case of opening/closing the screen.
3542 // In that case, we need to set the dimensions, but not the other
3543 // aspects.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003544 // If the text has been changed by webkit, update it. However, if
3545 // there has been more UI text input, ignore it. We will receive
3546 // another update when that text is recognized.
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003547 if (text != null && !text.equals(mWebTextView.getText().toString())
Cary Clarkd6982c92009-05-29 11:02:22 -04003548 && nativeTextGeneration() == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003549 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003550 }
3551 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003552 mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
3553 Gravity.RIGHT : Gravity.NO_GRAVITY);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003554 // This needs to be called before setType, which may call
3555 // requestFormData, and it needs to have the correct nodePointer.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003556 mWebTextView.setNodePointer(nodePointer);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003557 mWebTextView.setType(nativeFocusCandidateType());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003558 if (null == text) {
Cary Clark243ea062009-06-25 10:49:32 -04003559 if (DebugFlags.WEB_VIEW) {
3560 Log.v(LOGTAG, "rebuildWebTextView null == text");
3561 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003562 text = "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003563 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003564 mWebTextView.setTextAndKeepSelection(text);
Leon Scroggins5c84bf02010-02-09 17:03:44 -05003565 InputMethodManager imm = InputMethodManager.peekInstance();
3566 if (imm != null && imm.isActive(mWebTextView)) {
3567 imm.restartInput(mWebTextView);
3568 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003569 }
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003570 mWebTextView.requestFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003571 }
3572
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003573 /**
3574 * Called by WebTextView to find saved form data associated with the
3575 * textfield
3576 * @param name Name of the textfield.
3577 * @param nodePointer Pointer to the node of the textfield, so it can be
3578 * compared to the currently focused textfield when the data is
3579 * retrieved.
3580 */
3581 /* package */ void requestFormData(String name, int nodePointer) {
3582 if (mWebViewCore.getSettings().getSaveFormData()) {
3583 Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
3584 update.arg1 = nodePointer;
3585 RequestFormData updater = new RequestFormData(name, getUrl(),
3586 update);
3587 Thread t = new Thread(updater);
3588 t.start();
3589 }
3590 }
3591
Leon Scroggins3a503392010-01-06 17:04:38 -05003592 /**
3593 * Pass a message to find out the <label> associated with the <input>
3594 * identified by nodePointer
3595 * @param framePointer Pointer to the frame containing the <input> node
3596 * @param nodePointer Pointer to the node for which a <label> is desired.
3597 */
3598 /* package */ void requestLabel(int framePointer, int nodePointer) {
3599 mWebViewCore.sendMessage(EventHub.REQUEST_LABEL, framePointer,
3600 nodePointer);
3601 }
3602
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003603 /*
3604 * This class requests an Adapter for the WebTextView which shows past
3605 * entries stored in the database. It is a Runnable so that it can be done
3606 * in its own thread, without slowing down the UI.
3607 */
3608 private class RequestFormData implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003609 private String mName;
3610 private String mUrl;
3611 private Message mUpdateMessage;
3612
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003613 public RequestFormData(String name, String url, Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003614 mName = name;
3615 mUrl = url;
3616 mUpdateMessage = msg;
3617 }
3618
3619 public void run() {
3620 ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
3621 if (pastEntries.size() > 0) {
3622 AutoCompleteAdapter adapter = new
3623 AutoCompleteAdapter(mContext, pastEntries);
Cary Clarkded054c2009-06-15 10:26:08 -04003624 mUpdateMessage.obj = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003625 mUpdateMessage.sendToTarget();
3626 }
3627 }
3628 }
3629
Grace Kloba8ae1b412009-11-23 10:35:34 -08003630 /**
3631 * Dump the display tree to "/sdcard/displayTree.txt"
3632 *
3633 * @hide debug only
3634 */
3635 public void dumpDisplayTree() {
3636 nativeDumpDisplayTree(getUrl());
3637 }
3638
3639 /**
3640 * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
3641 * "/sdcard/domTree.txt"
3642 *
3643 * @hide debug only
3644 */
3645 public void dumpDomTree(boolean toFile) {
3646 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
3647 }
3648
3649 /**
3650 * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
3651 * to "/sdcard/renderTree.txt"
3652 *
3653 * @hide debug only
3654 */
3655 public void dumpRenderTree(boolean toFile) {
3656 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
3657 }
3658
Andrei Popescu5e7bb0a2010-02-01 22:32:16 +00003659 /**
3660 * Dump the V8 counters to standard output.
3661 * Note that you need a build with V8 and WEBCORE_INSTRUMENTATION set to
3662 * true. Otherwise, this will do nothing.
3663 *
3664 * @hide debug only
3665 */
3666 public void dumpV8Counters() {
3667 mWebViewCore.sendMessage(EventHub.DUMP_V8COUNTERS);
3668 }
3669
Leon Scrogginse3225672009-06-03 15:53:13 -04003670 // This is used to determine long press with the center key. Does not
3671 // affect long press with the trackball/touch.
3672 private boolean mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003673
3674 @Override
3675 public boolean onKeyDown(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003676 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003677 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003678 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003679 }
3680
3681 if (mNativeClass == 0) {
3682 return false;
3683 }
3684
3685 // do this hack up front, so it always works, regardless of touch-mode
3686 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
3687 mAutoRedraw = !mAutoRedraw;
3688 if (mAutoRedraw) {
3689 invalidate();
3690 }
3691 return true;
3692 }
3693
3694 // Bubble up the key event if
3695 // 1. it is a system key; or
Grace Kloba04b28682009-09-14 14:38:37 -07003696 // 2. the host application wants to handle it;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003697 if (event.isSystem()
Grace Kloba04b28682009-09-14 14:38:37 -07003698 || mCallbackProxy.uiOverrideKeyEvent(event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003699 return false;
3700 }
3701
Leon Scroggins7a375872010-03-09 14:26:07 -05003702 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
3703 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
3704 if (nativeFocusIsPlugin()) {
3705 mShiftIsPressed = true;
3706 } else if (!nativeCursorWantsKeyEvents() && !mShiftIsPressed) {
3707 setUpSelectXY();
3708 }
Cary Clark09e383c2009-10-26 16:43:58 -04003709 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003710
3711 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3712 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003713 switchOutDrawHistory();
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003714 if (nativeFocusIsPlugin()) {
3715 letPluginHandleNavKey(keyCode, event.getEventTime(), true);
3716 return true;
3717 }
Cary Clarkc05af372009-10-16 10:52:27 -04003718 if (mShiftIsPressed) {
3719 int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
3720 ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
3721 int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
3722 -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0;
3723 int multiplier = event.getRepeatCount() + 1;
3724 moveSelection(xRate * multiplier, yRate * multiplier);
3725 return true;
3726 }
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003727 if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003728 playSoundEffect(keyCodeToSoundsEffect(keyCode));
3729 return true;
3730 }
3731 // Bubble up the key event as WebView doesn't handle it
3732 return false;
3733 }
3734
Leon Scrogginse3225672009-06-03 15:53:13 -04003735 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003736 switchOutDrawHistory();
3737 if (event.getRepeatCount() == 0) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003738 if (mShiftIsPressed && !nativeFocusIsPlugin()) {
Cary Clarkc05af372009-10-16 10:52:27 -04003739 return true; // discard press if copy in progress
3740 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003741 mGotCenterDown = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003742 mPrivateHandler.sendMessageDelayed(mPrivateHandler
Leon Scrogginse3225672009-06-03 15:53:13 -04003743 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003744 // Already checked mNativeClass, so we do not need to check it
3745 // again.
3746 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
3747 return true;
3748 }
3749 // Bubble up the key event as WebView doesn't handle it
3750 return false;
3751 }
3752
Cary Clark843bbb82009-04-20 16:03:31 -04003753 if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
3754 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
3755 // turn off copy select if a shift-key combo is pressed
3756 mExtendSelection = mShiftIsPressed = false;
3757 if (mTouchMode == TOUCH_SELECT_MODE) {
3758 mTouchMode = TOUCH_INIT_MODE;
3759 }
3760 }
3761
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003762 if (getSettings().getNavDump()) {
3763 switch (keyCode) {
3764 case KeyEvent.KEYCODE_4:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003765 dumpDisplayTree();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003766 break;
3767 case KeyEvent.KEYCODE_5:
3768 case KeyEvent.KEYCODE_6:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003769 dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003770 break;
3771 case KeyEvent.KEYCODE_7:
3772 case KeyEvent.KEYCODE_8:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003773 dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003774 break;
3775 case KeyEvent.KEYCODE_9:
3776 nativeInstrumentReport();
3777 return true;
3778 }
3779 }
3780
Derek Sollenberger718d69f2009-10-19 15:56:43 -04003781 if (nativeCursorIsTextInput()) {
Leon Scroggins1cc24202009-06-16 10:10:28 -04003782 // This message will put the node in focus, for the DOM's notion
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003783 // of focus, and make the focuscontroller active
Leon Scroggins01058282009-07-30 16:33:56 -04003784 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
3785 nativeCursorNodePointer());
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003786 // This will bring up the WebTextView and put it in focus, for
3787 // our view system's notion of focus
3788 rebuildWebTextView();
3789 // Now we need to pass the event to it
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003790 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003791 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003792 return mWebTextView.dispatchKeyEvent(event);
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003793 }
Leon Scroggins40981262009-07-01 10:57:47 -04003794 } else if (nativeHasFocusNode()) {
3795 // In this case, the cursor is not on a text input, but the focus
3796 // might be. Check it, and if so, hand over to the WebTextView.
3797 rebuildWebTextView();
3798 if (inEditingMode()) {
Leon Scroggins0aa341f2010-02-24 16:49:35 -05003799 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003800 return mWebTextView.dispatchKeyEvent(event);
Leon Scroggins40981262009-07-01 10:57:47 -04003801 }
Cary Clark19436562009-06-04 16:25:07 -04003802 }
3803
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003804 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003805 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003806 // pass the key to DOM
3807 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
3808 // return true as DOM handles the key
3809 return true;
3810 }
3811
3812 // Bubble up the key event as WebView doesn't handle it
3813 return false;
3814 }
3815
3816 @Override
3817 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003818 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003819 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003820 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003821 }
3822
3823 if (mNativeClass == 0) {
3824 return false;
3825 }
3826
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003827 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04003828 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
3829 String text = nativeCursorText();
3830 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003831 && text.startsWith(SCHEME_TEL)) {
3832 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
3833 getContext().startActivity(intent);
3834 return true;
3835 }
3836 }
3837
3838 // Bubble up the key event if
3839 // 1. it is a system key; or
3840 // 2. the host application wants to handle it;
3841 if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
3842 return false;
3843 }
3844
Cary Clarkd6982c92009-05-29 11:02:22 -04003845 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003846 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
Leon Scroggins7a375872010-03-09 14:26:07 -05003847 if (nativeFocusIsPlugin()) {
3848 mShiftIsPressed = false;
Leon Scroggins7a375872010-03-09 14:26:07 -05003849 } else if (commitCopy()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003850 return true;
3851 }
3852 }
3853
3854 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3855 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003856 if (nativeFocusIsPlugin()) {
3857 letPluginHandleNavKey(keyCode, event.getEventTime(), false);
3858 return true;
3859 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003860 // always handle the navigation keys in the UI thread
3861 // Bubble up the key event as WebView doesn't handle it
3862 return false;
3863 }
3864
Leon Scrogginse3225672009-06-03 15:53:13 -04003865 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003866 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04003867 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
3868 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003869
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003870 if (mShiftIsPressed && !nativeFocusIsPlugin()) {
Cary Clarkc05af372009-10-16 10:52:27 -04003871 if (mExtendSelection) {
3872 commitCopy();
3873 } else {
3874 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04003875 invalidate(); // draw the i-beam instead of the arrow
Cary Clarkc05af372009-10-16 10:52:27 -04003876 }
3877 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003878 }
Grace Klobadd817492009-09-14 10:17:06 -07003879
3880 // perform the single click
3881 Rect visibleRect = sendOurVisibleRect();
3882 // Note that sendOurVisibleRect calls viewToContent, so the
3883 // coordinates should be in content coordinates.
3884 if (!nativeCursorIntersects(visibleRect)) {
3885 return false;
3886 }
Grace Klobadd817492009-09-14 10:17:06 -07003887 WebViewCore.CursorData data = cursorData();
3888 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
3889 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003890 if (nativeCursorIsTextInput()) {
3891 rebuildWebTextView();
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003892 centerKeyPressOnTextField();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003893 if (inEditingMode()) {
3894 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003895 }
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003896 return true;
3897 }
Leon Scroggins6088e832010-02-17 13:17:32 -05003898 clearTextEntry(true);
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003899 nativeSetFollowedLink(true);
3900 if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
Grace Klobadd817492009-09-14 10:17:06 -07003901 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
3902 nativeCursorNodePointer());
3903 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003904 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003905 }
3906
3907 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003908 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003909 // pass the key to DOM
3910 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
3911 // return true as DOM handles the key
3912 return true;
3913 }
3914
3915 // Bubble up the key event as WebView doesn't handle it
3916 return false;
3917 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003918
Cary Clark09e383c2009-10-26 16:43:58 -04003919 private void setUpSelectXY() {
3920 mExtendSelection = false;
3921 mShiftIsPressed = true;
3922 if (nativeHasCursorNode()) {
3923 Rect rect = nativeCursorNodeBounds();
3924 mSelectX = contentToViewX(rect.left);
3925 mSelectY = contentToViewY(rect.top);
3926 } else if (mLastTouchY > getVisibleTitleHeight()) {
3927 mSelectX = mScrollX + (int) mLastTouchX;
3928 mSelectY = mScrollY + (int) mLastTouchY;
3929 } else {
3930 mSelectX = mScrollX + getViewWidth() / 2;
3931 mSelectY = mScrollY + getViewHeightWithTitle() / 2;
3932 }
3933 nativeHideCursor();
3934 }
3935
Cary Clark966641a2010-03-04 08:41:56 -05003936 /**
3937 * Use this method to put the WebView into text selection mode.
3938 * Do not rely on this functionality; it will be deprecated in the future.
3939 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003940 public void emulateShiftHeld() {
Cary Clark7f970112009-10-15 15:29:08 -04003941 if (0 == mNativeClass) return; // client isn't initialized
Cary Clark09e383c2009-10-26 16:43:58 -04003942 setUpSelectXY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003943 }
3944
3945 private boolean commitCopy() {
3946 boolean copiedSomething = false;
3947 if (mExtendSelection) {
Cary Clark57d2c3a2009-12-23 13:57:15 -05003948 String selection = nativeGetSelection();
3949 if (selection != "") {
3950 if (DebugFlags.WEB_VIEW) {
3951 Log.v(LOGTAG, "commitCopy \"" + selection + "\"");
3952 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003953 Toast.makeText(mContext
3954 , com.android.internal.R.string.text_copied
3955 , Toast.LENGTH_SHORT).show();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003956 copiedSomething = true;
Cary Clark57d2c3a2009-12-23 13:57:15 -05003957 try {
3958 IClipboard clip = IClipboard.Stub.asInterface(
3959 ServiceManager.getService("clipboard"));
3960 clip.setClipboardText(selection);
3961 } catch (android.os.RemoteException e) {
3962 Log.e(LOGTAG, "Clipboard failed", e);
3963 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003964 }
3965 mExtendSelection = false;
3966 }
3967 mShiftIsPressed = false;
Cary Clark09e383c2009-10-26 16:43:58 -04003968 invalidate(); // remove selection region and pointer
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003969 if (mTouchMode == TOUCH_SELECT_MODE) {
3970 mTouchMode = TOUCH_INIT_MODE;
3971 }
3972 return copiedSomething;
3973 }
3974
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003975 @Override
3976 protected void onAttachedToWindow() {
3977 super.onAttachedToWindow();
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003978 if (hasWindowFocus()) onWindowFocusChanged(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003979 }
3980
3981 @Override
3982 protected void onDetachedFromWindow() {
Leon Scroggins6088e832010-02-17 13:17:32 -05003983 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003984 super.onDetachedFromWindow();
Grace Kloba21817f62010-03-19 18:28:33 -07003985 dismissZoomControl();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003986 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003987
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003988 /**
3989 * @deprecated WebView no longer needs to implement
3990 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
3991 */
3992 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003993 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04003994
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003995 /**
3996 * @deprecated WebView no longer needs to implement
3997 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
3998 */
3999 @Deprecated
4000 public void onChildViewRemoved(View p, View child) {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004001
4002 /**
4003 * @deprecated WebView should not have implemented
4004 * ViewTreeObserver.OnGlobalFocusChangeListener. This method
4005 * does nothing now.
4006 */
4007 @Deprecated
4008 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
4009 }
4010
Cary Clarkd6982c92009-05-29 11:02:22 -04004011 // To avoid drawing the cursor ring, and remove the TextView when our window
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004012 // loses focus.
4013 @Override
4014 public void onWindowFocusChanged(boolean hasWindowFocus) {
4015 if (hasWindowFocus) {
4016 if (hasFocus()) {
4017 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004018 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04004019 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004020 if (mNativeClass != 0) {
4021 nativeRecordButtons(true, false, true);
Leon Scroggins8cdad882009-06-30 08:47:04 -04004022 if (inEditingMode()) {
4023 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
4024 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004025 }
4026 } else {
4027 // If our window gained focus, but we do not have it, do not
Cary Clarkd6982c92009-05-29 11:02:22 -04004028 // draw the cursor ring.
4029 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004030 // We do not call nativeRecordButtons here because we assume
4031 // that when we lost focus, or window focus, it got called with
4032 // false for the first parameter
4033 }
4034 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07004035 if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004036 /*
4037 * The zoom controls come in their own window, so our window
Cary Clarkd6982c92009-05-29 11:02:22 -04004038 * loses focus. Our policy is to not draw the cursor ring if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004039 * our window is not focused, but this is an exception since
4040 * the user can still navigate the web page with the zoom
4041 * controls showing.
4042 */
Cary Clarkd6982c92009-05-29 11:02:22 -04004043 // If our window has lost focus, stop drawing the cursor ring
4044 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004045 }
4046 mGotKeyDown = false;
4047 mShiftIsPressed = false;
4048 if (mNativeClass != 0) {
4049 nativeRecordButtons(false, false, true);
4050 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004051 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004052 }
4053 invalidate();
4054 super.onWindowFocusChanged(hasWindowFocus);
4055 }
4056
Leon Scroggins63dda1c2009-04-15 13:25:00 -04004057 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004058 * Pass a message to WebCore Thread, telling the WebCore::Page's
4059 * FocusController to be "inactive" so that it will
4060 * not draw the blinking cursor. It gets set to "active" to draw the cursor
4061 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04004062 */
Leon Scroggins01058282009-07-30 16:33:56 -04004063 /* package */ void setFocusControllerInactive() {
Leon Scrogginsfd06bc82009-06-08 13:22:02 -04004064 // Do not need to also check whether mWebViewCore is null, because
4065 // mNativeClass is only set if mWebViewCore is non null
4066 if (mNativeClass == 0) return;
Leon Scroggins8cdad882009-06-30 08:47:04 -04004067 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
Leon Scroggins63dda1c2009-04-15 13:25:00 -04004068 }
4069
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004070 @Override
4071 protected void onFocusChanged(boolean focused, int direction,
4072 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004073 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004074 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
4075 }
4076 if (focused) {
4077 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004078 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004079 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004080 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004081 if (mNativeClass != 0) {
4082 nativeRecordButtons(true, false, true);
4083 }
4084 //} else {
4085 // The WebView has gained focus while we do not have
4086 // windowfocus. When our window lost focus, we should have
4087 // called nativeRecordButtons(false...)
4088 }
4089 } else {
4090 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04004091 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004092 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004093 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004094 if (mNativeClass != 0) {
4095 nativeRecordButtons(false, false, true);
4096 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004097 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004098 }
4099 mGotKeyDown = false;
4100 }
4101
4102 super.onFocusChanged(focused, direction, previouslyFocusedRect);
4103 }
4104
Grace Kloba3f9faf42009-10-13 14:13:54 -07004105 /**
4106 * @hide
4107 */
4108 @Override
4109 protected boolean setFrame(int left, int top, int right, int bottom) {
4110 boolean changed = super.setFrame(left, top, right, bottom);
4111 if (!changed && mHeightCanMeasure) {
4112 // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
4113 // in WebViewCore after we get the first layout. We do call
4114 // requestLayout() when we get contentSizeChanged(). But the View
4115 // system won't call onSizeChanged if the dimension is not changed.
4116 // In this case, we need to call sendViewSizeZoom() explicitly to
4117 // notify the WebKit about the new dimensions.
4118 sendViewSizeZoom();
4119 }
4120 return changed;
4121 }
4122
Grace Kloba3a0def22010-01-23 21:11:54 -08004123 private static class PostScale implements Runnable {
4124 final WebView mWebView;
4125 final boolean mUpdateTextWrap;
4126
4127 public PostScale(WebView webView, boolean updateTextWrap) {
4128 mWebView = webView;
4129 mUpdateTextWrap = updateTextWrap;
4130 }
4131
4132 public void run() {
4133 if (mWebView.mWebViewCore != null) {
4134 // we always force, in case our height changed, in which case we
4135 // still want to send the notification over to webkit.
4136 mWebView.setNewZoomScale(mWebView.mActualScale,
4137 mUpdateTextWrap, true);
Grace Klobab3d0cc52010-03-01 12:25:29 -08004138 // update the zoom buttons as the scale can be changed
4139 if (mWebView.getSettings().getBuiltInZoomControls()) {
4140 mWebView.updateZoomButtonsEnabled();
4141 }
Grace Kloba3a0def22010-01-23 21:11:54 -08004142 }
4143 }
4144 }
4145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004146 @Override
4147 protected void onSizeChanged(int w, int h, int ow, int oh) {
4148 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004149 // Center zooming to the center of the screen.
Cary Clarka91874d2009-08-24 14:08:43 -04004150 if (mZoomScale == 0) { // unless we're already zooming
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08004151 // To anchor at top left corner.
4152 mZoomCenterX = 0;
4153 mZoomCenterY = getVisibleTitleHeight();
Grace Kloba3a0def22010-01-23 21:11:54 -08004154 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
4155 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Cary Clarka91874d2009-08-24 14:08:43 -04004156 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004157
Grace Klobaa4fa1072009-11-09 12:01:50 -08004158 // adjust the max viewport width depending on the view dimensions. This
4159 // is to ensure the scaling is not going insane. So do not shrink it if
4160 // the view size is temporarily smaller, e.g. when soft keyboard is up.
4161 int newMaxViewportWidth = (int) (Math.max(w, h) / DEFAULT_MIN_ZOOM_SCALE);
4162 if (newMaxViewportWidth > sMaxViewportWidth) {
4163 sMaxViewportWidth = newMaxViewportWidth;
4164 }
4165
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004166 // update mMinZoomScale if the minimum zoom scale is not fixed
4167 if (!mMinZoomScaleFixed) {
Grace Klobaba672802009-09-25 15:54:08 -07004168 // when change from narrow screen to wide screen, the new viewWidth
4169 // can be wider than the old content width. We limit the minimum
4170 // scale to 1.0f. The proper minimum scale will be calculated when
4171 // the new picture shows up.
4172 mMinZoomScale = Math.min(1.0f, (float) getViewWidth()
Grace Klobae397a882009-08-06 12:04:14 -07004173 / (mDrawHistory ? mHistoryPicture.getWidth()
Grace Klobaba672802009-09-25 15:54:08 -07004174 : mZoomOverviewWidth));
Grace Kloba16efce72009-11-10 15:49:03 -08004175 if (mInitialScaleInPercent > 0) {
4176 // limit the minZoomScale to the initialScale if it is set
4177 float initialScale = mInitialScaleInPercent / 100.0f;
4178 if (mMinZoomScale > initialScale) {
4179 mMinZoomScale = initialScale;
4180 }
4181 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004182 }
4183
Grace Kloba21817f62010-03-19 18:28:33 -07004184 dismissZoomControl();
4185
Grace Kloba70b942d2009-12-11 19:33:04 -08004186 // onSizeChanged() is called during WebView layout. And any
4187 // requestLayout() is blocked during layout. As setNewZoomScale() will
4188 // call its child View to reposition itself through ViewManager's
4189 // scaleAll(), we need to post a Runnable to ensure requestLayout().
Grace Kloba3a0def22010-01-23 21:11:54 -08004190 // <b/>
4191 // only update the text wrap scale if width changed.
4192 post(new PostScale(this, w != ow));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004193 }
4194
4195 @Override
4196 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
4197 super.onScrollChanged(l, t, oldl, oldt);
4198 sendOurVisibleRect();
4199 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004201 @Override
4202 public boolean dispatchKeyEvent(KeyEvent event) {
4203 boolean dispatch = true;
4204
Leon Scroggins7a375872010-03-09 14:26:07 -05004205 // Textfields and plugins need to receive the shift up key even if
4206 // another key was released while the shift key was held down.
Cary Clarka91e9722010-03-12 09:13:53 -05004207 if (!inEditingMode() && (mNativeClass == 0 || !nativeFocusIsPlugin())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004208 if (event.getAction() == KeyEvent.ACTION_DOWN) {
4209 mGotKeyDown = true;
4210 } else {
4211 if (!mGotKeyDown) {
4212 /*
4213 * We got a key up for which we were not the recipient of
4214 * the original key down. Don't give it to the view.
4215 */
4216 dispatch = false;
4217 }
4218 mGotKeyDown = false;
4219 }
4220 }
4221
4222 if (dispatch) {
4223 return super.dispatchKeyEvent(event);
4224 } else {
4225 // We didn't dispatch, so let something else handle the key
4226 return false;
4227 }
4228 }
4229
4230 // Here are the snap align logic:
4231 // 1. If it starts nearly horizontally or vertically, snap align;
4232 // 2. If there is a dramitic direction change, let it go;
4233 // 3. If there is a same direction back and forth, lock it.
4234
4235 // adjustable parameters
4236 private int mMinLockSnapReverseDistance;
4237 private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
4238 private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
4239
Mike Reed19f3f0e2009-11-12 12:50:20 -05004240 private static int sign(float x) {
4241 return x > 0 ? 1 : (x < 0 ? -1 : 0);
4242 }
4243
4244 // if the page can scroll <= this value, we won't allow the drag tracker
4245 // to have any effect.
4246 private static final int MIN_SCROLL_AMOUNT_TO_DISABLE_DRAG_TRACKER = 4;
4247
4248 private class DragTrackerHandler {
4249 private final DragTracker mProxy;
4250 private final float mStartY, mStartX;
4251 private final float mMinDY, mMinDX;
4252 private final float mMaxDY, mMaxDX;
4253 private float mCurrStretchY, mCurrStretchX;
4254 private int mSX, mSY;
Mike Reedc95326092010-02-08 16:49:58 -05004255 private Interpolator mInterp;
4256 private float[] mXY = new float[2];
4257
4258 // inner (non-state) classes can't have enums :(
4259 private static final int DRAGGING_STATE = 0;
4260 private static final int ANIMATING_STATE = 1;
4261 private static final int FINISHED_STATE = 2;
4262 private int mState;
Mike Reed19f3f0e2009-11-12 12:50:20 -05004263
4264 public DragTrackerHandler(float x, float y, DragTracker proxy) {
4265 mProxy = proxy;
4266
4267 int docBottom = computeVerticalScrollRange() + getTitleHeight();
4268 int viewTop = getScrollY();
4269 int viewBottom = viewTop + getHeight();
4270
4271 mStartY = y;
4272 mMinDY = -viewTop;
4273 mMaxDY = docBottom - viewBottom;
4274
Mike Reedfdc54242010-01-08 10:29:51 -05004275 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004276 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " dragtracker y= " + y +
4277 " up/down= " + mMinDY + " " + mMaxDY);
4278 }
4279
4280 int docRight = computeHorizontalScrollRange();
4281 int viewLeft = getScrollX();
4282 int viewRight = viewLeft + getWidth();
4283 mStartX = x;
4284 mMinDX = -viewLeft;
4285 mMaxDX = docRight - viewRight;
4286
Mike Reedc95326092010-02-08 16:49:58 -05004287 mState = DRAGGING_STATE;
Mike Reed19f3f0e2009-11-12 12:50:20 -05004288 mProxy.onStartDrag(x, y);
4289
4290 // ensure we buildBitmap at least once
4291 mSX = -99999;
4292 }
4293
4294 private float computeStretch(float delta, float min, float max) {
4295 float stretch = 0;
4296 if (max - min > MIN_SCROLL_AMOUNT_TO_DISABLE_DRAG_TRACKER) {
4297 if (delta < min) {
4298 stretch = delta - min;
4299 } else if (delta > max) {
4300 stretch = delta - max;
4301 }
4302 }
4303 return stretch;
4304 }
4305
4306 public void dragTo(float x, float y) {
4307 float sy = computeStretch(mStartY - y, mMinDY, mMaxDY);
4308 float sx = computeStretch(mStartX - x, mMinDX, mMaxDX);
4309
Mike Reedc95326092010-02-08 16:49:58 -05004310 if ((mSnapScrollMode & SNAP_X) != 0) {
4311 sy = 0;
4312 } else if ((mSnapScrollMode & SNAP_Y) != 0) {
4313 sx = 0;
4314 }
4315
Mike Reed19f3f0e2009-11-12 12:50:20 -05004316 if (mCurrStretchX != sx || mCurrStretchY != sy) {
4317 mCurrStretchX = sx;
4318 mCurrStretchY = sy;
Mike Reedfdc54242010-01-08 10:29:51 -05004319 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004320 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "---- stretch " + sx +
4321 " " + sy);
4322 }
4323 if (mProxy.onStretchChange(sx, sy)) {
4324 invalidate();
4325 }
4326 }
4327 }
4328
4329 public void stopDrag() {
Mike Reedc95326092010-02-08 16:49:58 -05004330 final int DURATION = 200;
4331 int now = (int)SystemClock.uptimeMillis();
4332 mInterp = new Interpolator(2);
4333 mXY[0] = mCurrStretchX;
4334 mXY[1] = mCurrStretchY;
4335 // float[] blend = new float[] { 0.5f, 0, 0.75f, 1 };
4336 float[] blend = new float[] { 0, 0.5f, 0.75f, 1 };
4337 mInterp.setKeyFrame(0, now, mXY, blend);
4338 float[] zerozero = new float[] { 0, 0 };
4339 mInterp.setKeyFrame(1, now + DURATION, zerozero, null);
4340 mState = ANIMATING_STATE;
4341
Mike Reedfdc54242010-01-08 10:29:51 -05004342 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reedc95326092010-02-08 16:49:58 -05004343 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag, starting animation");
Mike Reed19f3f0e2009-11-12 12:50:20 -05004344 }
Mike Reedc95326092010-02-08 16:49:58 -05004345 }
4346
4347 // Call this after each draw. If it ruturns null, the tracker is done
4348 public boolean isFinished() {
4349 return mState == FINISHED_STATE;
Mike Reed19f3f0e2009-11-12 12:50:20 -05004350 }
4351
4352 private int hiddenHeightOfTitleBar() {
4353 return getTitleHeight() - getVisibleTitleHeight();
4354 }
4355
4356 // need a way to know if 565 or 8888 is the right config for
4357 // capturing the display and giving it to the drag proxy
4358 private Bitmap.Config offscreenBitmapConfig() {
4359 // hard code 565 for now
4360 return Bitmap.Config.RGB_565;
4361 }
4362
4363 /* If the tracker draws, then this returns true, otherwise it will
4364 return false, and draw nothing.
4365 */
4366 public boolean draw(Canvas canvas) {
4367 if (mCurrStretchX != 0 || mCurrStretchY != 0) {
4368 int sx = getScrollX();
4369 int sy = getScrollY() - hiddenHeightOfTitleBar();
Mike Reed19f3f0e2009-11-12 12:50:20 -05004370 if (mSX != sx || mSY != sy) {
4371 buildBitmap(sx, sy);
4372 mSX = sx;
4373 mSY = sy;
4374 }
4375
Mike Reedc95326092010-02-08 16:49:58 -05004376 if (mState == ANIMATING_STATE) {
4377 Interpolator.Result result = mInterp.timeToValues(mXY);
4378 if (result == Interpolator.Result.FREEZE_END) {
4379 mState = FINISHED_STATE;
4380 return false;
4381 } else {
4382 mProxy.onStretchChange(mXY[0], mXY[1]);
4383 invalidate();
4384 // fall through to the draw
4385 }
4386 }
Mike Reed19f3f0e2009-11-12 12:50:20 -05004387 int count = canvas.save(Canvas.MATRIX_SAVE_FLAG);
4388 canvas.translate(sx, sy);
4389 mProxy.onDraw(canvas);
4390 canvas.restoreToCount(count);
4391 return true;
4392 }
Mike Reedfdc54242010-01-08 10:29:51 -05004393 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004394 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " -- draw false " +
4395 mCurrStretchX + " " + mCurrStretchY);
4396 }
4397 return false;
4398 }
4399
4400 private void buildBitmap(int sx, int sy) {
4401 int w = getWidth();
4402 int h = getViewHeight();
4403 Bitmap bm = Bitmap.createBitmap(w, h, offscreenBitmapConfig());
4404 Canvas canvas = new Canvas(bm);
4405 canvas.translate(-sx, -sy);
4406 drawContent(canvas);
4407
Mike Reedfdc54242010-01-08 10:29:51 -05004408 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004409 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "--- buildBitmap " + sx +
4410 " " + sy + " " + w + " " + h);
4411 }
4412 mProxy.onBitmapChange(bm);
4413 }
4414 }
4415
4416 /** @hide */
4417 public static class DragTracker {
4418 public void onStartDrag(float x, float y) {}
4419 public boolean onStretchChange(float sx, float sy) {
4420 // return true to have us inval the view
4421 return false;
4422 }
4423 public void onStopDrag() {}
4424 public void onBitmapChange(Bitmap bm) {}
4425 public void onDraw(Canvas canvas) {}
4426 }
4427
4428 /** @hide */
4429 public DragTracker getDragTracker() {
4430 return mDragTracker;
4431 }
4432
4433 /** @hide */
4434 public void setDragTracker(DragTracker tracker) {
4435 mDragTracker = tracker;
4436 }
4437
4438 private DragTracker mDragTracker;
4439 private DragTrackerHandler mDragTrackerHandler;
4440
Grace Kloba3a0def22010-01-23 21:11:54 -08004441 private class ScaleDetectorListener implements
4442 ScaleGestureDetector.OnScaleGestureListener {
4443
4444 public boolean onScaleBegin(ScaleGestureDetector detector) {
4445 // cancel the single touch handling
4446 cancelTouch();
Grace Kloba21817f62010-03-19 18:28:33 -07004447 dismissZoomControl();
Grace Kloba3a0def22010-01-23 21:11:54 -08004448 // reset the zoom overview mode so that the page won't auto grow
4449 mInZoomOverview = false;
4450 // If it is in password mode, turn it off so it does not draw
4451 // misplaced.
4452 if (inEditingMode() && nativeFocusCandidateIsPassword()) {
4453 mWebTextView.setInPassword(false);
4454 }
4455 return true;
4456 }
4457
4458 public void onScaleEnd(ScaleGestureDetector detector) {
4459 if (mPreviewZoomOnly) {
4460 mPreviewZoomOnly = false;
4461 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
4462 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
4463 // don't reflow when zoom in; when zoom out, do reflow if the
4464 // new scale is almost minimum scale;
Grace Klobac6f95fe2010-03-10 13:25:34 -08004465 boolean reflowNow = (mActualScale - mMinZoomScale
4466 <= MINIMUM_SCALE_INCREMENT)
Grace Kloba3a0def22010-01-23 21:11:54 -08004467 || ((mActualScale <= 0.8 * mTextWrapScale));
4468 // force zoom after mPreviewZoomOnly is set to false so that the
4469 // new view size will be passed to the WebKit
4470 setNewZoomScale(mActualScale, reflowNow, true);
4471 // call invalidate() to draw without zoom filter
4472 invalidate();
4473 }
4474 // adjust the edit text view if needed
4475 if (inEditingMode() && didUpdateTextViewBounds(false)
4476 && nativeFocusCandidateIsPassword()) {
4477 // If it is a password field, start drawing the
4478 // WebTextView once again.
4479 mWebTextView.setInPassword(true);
4480 }
4481 // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as it
4482 // may trigger the unwanted click, can't use TOUCH_DRAG_MODE as it
4483 // may trigger the unwanted fling.
4484 mTouchMode = TOUCH_PINCH_DRAG;
Grace Klobac2242f22010-03-05 14:00:26 -08004485 mConfirmMove = true;
Grace Kloba3a0def22010-01-23 21:11:54 -08004486 startTouch(detector.getFocusX(), detector.getFocusY(),
4487 mLastTouchTime);
4488 }
4489
4490 public boolean onScale(ScaleGestureDetector detector) {
4491 float scale = (float) (Math.round(detector.getScaleFactor()
4492 * mActualScale * 100) / 100.0);
Grace Klobac6f95fe2010-03-10 13:25:34 -08004493 if (Math.abs(scale - mActualScale) >= MINIMUM_SCALE_INCREMENT) {
Grace Kloba3a0def22010-01-23 21:11:54 -08004494 mPreviewZoomOnly = true;
4495 // limit the scale change per step
4496 if (scale > mActualScale) {
4497 scale = Math.min(scale, mActualScale * 1.25f);
4498 } else {
4499 scale = Math.max(scale, mActualScale * 0.8f);
4500 }
4501 mZoomCenterX = detector.getFocusX();
4502 mZoomCenterY = detector.getFocusY();
4503 setNewZoomScale(scale, false, false);
4504 invalidate();
4505 return true;
4506 }
4507 return false;
4508 }
4509 }
4510
Grace Klobac2242f22010-03-05 14:00:26 -08004511 private boolean hitFocusedPlugin(int contentX, int contentY) {
Cary Clarke84a0db2010-03-17 15:58:20 -04004512 if (DebugFlags.WEB_VIEW) {
4513 Log.v(LOGTAG, "nativeFocusIsPlugin()=" + nativeFocusIsPlugin());
4514 Rect r = nativeFocusNodeBounds();
4515 Log.v(LOGTAG, "nativeFocusNodeBounds()=(" + r.left + ", " + r.top
4516 + ", " + r.right + ", " + r.bottom + ")");
4517 }
Grace Klobac2242f22010-03-05 14:00:26 -08004518 return nativeFocusIsPlugin()
Cary Clarke84a0db2010-03-17 15:58:20 -04004519 && nativeFocusNodeBounds().contains(contentX, contentY);
Grace Klobac2242f22010-03-05 14:00:26 -08004520 }
4521
4522 private boolean shouldForwardTouchEvent() {
4523 return mFullScreenHolder != null || (mForwardTouchEvents
4524 && mTouchMode != TOUCH_SELECT_MODE
4525 && mPreventDefault != PREVENT_DEFAULT_IGNORE);
4526 }
4527
4528 private boolean inFullScreenMode() {
4529 return mFullScreenHolder != null;
4530 }
4531
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004532 @Override
4533 public boolean onTouchEvent(MotionEvent ev) {
4534 if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
4535 return false;
4536 }
4537
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004538 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004539 Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
4540 + mTouchMode);
4541 }
4542
Grace Kloba3a0def22010-01-23 21:11:54 -08004543 int action;
4544 float x, y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004545 long eventTime = ev.getEventTime();
4546
Grace Kloba3a0def22010-01-23 21:11:54 -08004547 // FIXME: we may consider to give WebKit an option to handle multi-touch
4548 // events later.
4549 if (mSupportMultiTouch && ev.getPointerCount() > 1) {
4550 if (mMinZoomScale < mMaxZoomScale) {
4551 mScaleDetector.onTouchEvent(ev);
4552 if (mScaleDetector.isInProgress()) {
4553 mLastTouchTime = eventTime;
4554 return true;
4555 }
4556 x = mScaleDetector.getFocusX();
4557 y = mScaleDetector.getFocusY();
4558 action = ev.getAction() & MotionEvent.ACTION_MASK;
4559 if (action == MotionEvent.ACTION_POINTER_DOWN) {
4560 cancelTouch();
4561 action = MotionEvent.ACTION_DOWN;
4562 } else if (action == MotionEvent.ACTION_POINTER_UP) {
4563 // set mLastTouchX/Y to the remaining point
4564 mLastTouchX = x;
4565 mLastTouchY = y;
4566 } else if (action == MotionEvent.ACTION_MOVE) {
4567 // negative x or y indicate it is on the edge, skip it.
4568 if (x < 0 || y < 0) {
4569 return true;
4570 }
4571 }
4572 } else {
4573 // if the page disallow zoom, skip multi-pointer action
4574 return true;
4575 }
4576 } else {
4577 action = ev.getAction();
4578 x = ev.getX();
4579 y = ev.getY();
4580 }
4581
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004582 // Due to the touch screen edge effect, a touch closer to the edge
4583 // always snapped to the edge. As getViewWidth() can be different from
4584 // getWidth() due to the scrollbar, adjusting the point to match
4585 // getViewWidth(). Same applied to the height.
4586 if (x > getViewWidth() - 1) {
4587 x = getViewWidth() - 1;
4588 }
Grace Kloba8eff73f2009-09-24 09:34:32 -07004589 if (y > getViewHeightWithTitle() - 1) {
4590 y = getViewHeightWithTitle() - 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004591 }
4592
Cary Clark25415e22009-10-12 13:41:28 -04004593 float fDeltaX = mLastTouchX - x;
4594 float fDeltaY = mLastTouchY - y;
4595 int deltaX = (int) fDeltaX;
4596 int deltaY = (int) fDeltaY;
Grace Klobac2242f22010-03-05 14:00:26 -08004597 int contentX = viewToContentX((int) x + mScrollX);
4598 int contentY = viewToContentY((int) y + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004599
4600 switch (action) {
4601 case MotionEvent.ACTION_DOWN: {
Grace Klobac2242f22010-03-05 14:00:26 -08004602 mPreventDefault = PREVENT_DEFAULT_NO;
4603 mConfirmMove = false;
Grace Kloba04b28682009-09-14 14:38:37 -07004604 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04004605 // stop the current scroll animation, but if this is
4606 // the start of a fling, allow it to add to the current
4607 // fling's velocity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004608 mScroller.abortAnimation();
4609 mTouchMode = TOUCH_DRAG_START_MODE;
Grace Klobac2242f22010-03-05 14:00:26 -08004610 mConfirmMove = true;
Grace Kloba96949ef2010-01-25 09:53:01 -08004611 mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
Grace Klobac2242f22010-03-05 14:00:26 -08004612 } else if (!inFullScreenMode() && mShiftIsPressed) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004613 mSelectX = mScrollX + (int) x;
4614 mSelectY = mScrollY + (int) y;
4615 mTouchMode = TOUCH_SELECT_MODE;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004616 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004617 Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
4618 }
Grace Klobac2242f22010-03-05 14:00:26 -08004619 nativeMoveSelection(contentX, contentY, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004620 mTouchSelection = mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04004621 invalidate(); // draw the i-beam instead of the arrow
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004622 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
4623 mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
4624 if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
4625 mTouchMode = TOUCH_DOUBLE_TAP_MODE;
4626 } else {
4627 // commit the short press action for the previous tap
4628 doShortPress();
Grace Klobac2242f22010-03-05 14:00:26 -08004629 mTouchMode = TOUCH_INIT_MODE;
4630 mDeferTouchProcess = (!inFullScreenMode()
4631 && mForwardTouchEvents) ? hitFocusedPlugin(
4632 contentX, contentY) : false;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004633 }
Grace Klobac2242f22010-03-05 14:00:26 -08004634 } else { // the normal case
Grace Kloba3a0def22010-01-23 21:11:54 -08004635 mPreviewZoomOnly = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004636 mTouchMode = TOUCH_INIT_MODE;
Grace Klobac2242f22010-03-05 14:00:26 -08004637 mDeferTouchProcess = (!inFullScreenMode()
4638 && mForwardTouchEvents) ? hitFocusedPlugin(
4639 contentX, contentY) : false;
Cary Clark77d98f42009-07-31 09:40:38 -04004640 mWebViewCore.sendMessage(
4641 EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004642 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
Dan Egnor18e93962010-02-10 19:27:58 -08004643 EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004644 (eventTime - mLastTouchUpTime), eventTime);
4645 }
4646 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004647 // Trigger the link
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004648 if (mTouchMode == TOUCH_INIT_MODE
4649 || mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
Grace Klobac2242f22010-03-05 14:00:26 -08004650 mPrivateHandler.sendEmptyMessageDelayed(
4651 SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
4652 mPrivateHandler.sendEmptyMessageDelayed(
4653 SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT);
4654 if (inFullScreenMode() || mDeferTouchProcess) {
4655 mPreventDefault = PREVENT_DEFAULT_YES;
4656 } else if (mForwardTouchEvents) {
4657 mPreventDefault = PREVENT_DEFAULT_MAYBE_YES;
4658 } else {
4659 mPreventDefault = PREVENT_DEFAULT_NO;
4660 }
4661 // pass the touch events from UI thread to WebCore thread
4662 if (shouldForwardTouchEvent()) {
4663 TouchEventData ted = new TouchEventData();
4664 ted.mAction = action;
4665 ted.mX = contentX;
4666 ted.mY = contentY;
4667 ted.mMetaState = ev.getMetaState();
4668 ted.mReprocess = mDeferTouchProcess;
4669 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
4670 if (mDeferTouchProcess) {
4671 // still needs to set them for compute deltaX/Y
4672 mLastTouchX = x;
4673 mLastTouchY = y;
4674 break;
4675 }
4676 if (!inFullScreenMode()) {
4677 mPrivateHandler.sendMessageDelayed(mPrivateHandler
4678 .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
4679 action, 0), TAP_TIMEOUT);
4680 }
4681 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004682 }
Grace Kloba3a0def22010-01-23 21:11:54 -08004683 startTouch(x, y, eventTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004684 break;
4685 }
4686 case MotionEvent.ACTION_MOVE: {
Grace Klobac2242f22010-03-05 14:00:26 -08004687 boolean firstMove = false;
4688 if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
4689 >= mTouchSlopSquare) {
4690 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4691 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
4692 mConfirmMove = true;
4693 firstMove = true;
4694 if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
4695 mTouchMode = TOUCH_INIT_MODE;
4696 }
4697 }
4698 // pass the touch events from UI thread to WebCore thread
4699 if (shouldForwardTouchEvent() && mConfirmMove && (firstMove
4700 || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
4701 TouchEventData ted = new TouchEventData();
4702 ted.mAction = action;
4703 ted.mX = contentX;
4704 ted.mY = contentY;
4705 ted.mMetaState = ev.getMetaState();
4706 ted.mReprocess = mDeferTouchProcess;
4707 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
4708 mLastSentTouchTime = eventTime;
4709 if (mDeferTouchProcess) {
4710 break;
4711 }
4712 if (firstMove && !inFullScreenMode()) {
4713 mPrivateHandler.sendMessageDelayed(mPrivateHandler
4714 .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
4715 action, 0), TAP_TIMEOUT);
4716 }
4717 }
4718 if (mTouchMode == TOUCH_DONE_MODE
4719 || mPreventDefault == PREVENT_DEFAULT_YES) {
4720 // no dragging during scroll zoom animation, or when prevent
4721 // default is yes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004722 break;
4723 }
Grace Klobac2242f22010-03-05 14:00:26 -08004724 if (mVelocityTracker == null) {
4725 Log.e(LOGTAG, "Got null mVelocityTracker when "
4726 + "mPreventDefault = " + mPreventDefault
4727 + " mDeferTouchProcess = " + mDeferTouchProcess
4728 + " mTouchMode = " + mTouchMode);
4729 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004730 mVelocityTracker.addMovement(ev);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004731 if (mTouchMode != TOUCH_DRAG_MODE) {
4732 if (mTouchMode == TOUCH_SELECT_MODE) {
4733 mSelectX = mScrollX + (int) x;
4734 mSelectY = mScrollY + (int) y;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004735 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004736 Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
4737 }
Grace Klobac2242f22010-03-05 14:00:26 -08004738 nativeMoveSelection(contentX, contentY, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004739 invalidate();
4740 break;
4741 }
Grace Klobac2242f22010-03-05 14:00:26 -08004742 if (!mConfirmMove) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004743 break;
4744 }
Grace Klobac2242f22010-03-05 14:00:26 -08004745 if (mPreventDefault == PREVENT_DEFAULT_MAYBE_YES
4746 || mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
Grace Klobaf58af622009-09-24 17:41:23 -07004747 // track mLastTouchTime as we may need to do fling at
4748 // ACTION_UP
4749 mLastTouchTime = eventTime;
4750 break;
4751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004752 // if it starts nearly horizontal or vertical, enforce it
4753 int ax = Math.abs(deltaX);
4754 int ay = Math.abs(deltaY);
4755 if (ax > MAX_SLOPE_FOR_DIAG * ay) {
4756 mSnapScrollMode = SNAP_X;
4757 mSnapPositive = deltaX > 0;
4758 } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
4759 mSnapScrollMode = SNAP_Y;
4760 mSnapPositive = deltaY > 0;
4761 }
4762
4763 mTouchMode = TOUCH_DRAG_MODE;
Romain Guyf7b4acc2009-12-01 16:24:45 -08004764 mLastTouchX = x;
4765 mLastTouchY = y;
4766 fDeltaX = 0.0f;
4767 fDeltaY = 0.0f;
4768 deltaX = 0;
4769 deltaY = 0;
4770
Grace Klobac2242f22010-03-05 14:00:26 -08004771 startDrag();
4772 }
4773
4774 if (mDragTrackerHandler != null) {
4775 mDragTrackerHandler.dragTo(x, y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004776 }
4777
4778 // do pan
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004779 boolean done = false;
Cary Clark25415e22009-10-12 13:41:28 -04004780 boolean keepScrollBarsVisible = false;
4781 if (Math.abs(fDeltaX) < 1.0f && Math.abs(fDeltaY) < 1.0f) {
4782 keepScrollBarsVisible = done = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004783 } else {
4784 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
4785 int ax = Math.abs(deltaX);
4786 int ay = Math.abs(deltaY);
4787 if (mSnapScrollMode == SNAP_X) {
4788 // radical change means getting out of snap mode
4789 if (ay > MAX_SLOPE_FOR_DIAG * ax
4790 && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
4791 mSnapScrollMode = SNAP_NONE;
4792 }
4793 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004794 if (ax > MAX_SLOPE_FOR_DIAG * ay &&
4795 (mSnapPositive
4796 ? deltaX < -mMinLockSnapReverseDistance
4797 : deltaX > mMinLockSnapReverseDistance)) {
4798 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004799 }
4800 } else {
4801 // radical change means getting out of snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004802 if (ax > MAX_SLOPE_FOR_DIAG * ay
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004803 && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
4804 mSnapScrollMode = SNAP_NONE;
4805 }
4806 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04004807 if (ay > MAX_SLOPE_FOR_DIAG * ax &&
4808 (mSnapPositive
4809 ? deltaY < -mMinLockSnapReverseDistance
4810 : deltaY > mMinLockSnapReverseDistance)) {
4811 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004812 }
4813 }
4814 }
Cary Clarkac492e12009-10-14 14:53:37 -04004815 if (mSnapScrollMode != SNAP_NONE) {
4816 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
4817 deltaY = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07004818 } else {
Cary Clarkac492e12009-10-14 14:53:37 -04004819 deltaX = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07004820 }
Cary Clarkac492e12009-10-14 14:53:37 -04004821 }
4822 if ((deltaX | deltaY) != 0) {
Cary Clarkac492e12009-10-14 14:53:37 -04004823 if (deltaX != 0) {
4824 mLastTouchX = x;
4825 }
4826 if (deltaY != 0) {
4827 mLastTouchY = y;
4828 }
4829 mHeldMotionless = MOTIONLESS_FALSE;
4830 } else {
4831 // keep the scrollbar on the screen even there is no
4832 // scroll
4833 keepScrollBarsVisible = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004834 }
4835 mLastTouchTime = eventTime;
4836 mUserScroll = true;
4837 }
The Android Open Source Project10592532009-03-18 17:39:46 -07004838
Grace Klobac2242f22010-03-05 14:00:26 -08004839 doDrag(deltaX, deltaY);
Mike Reed19f3f0e2009-11-12 12:50:20 -05004840
Cary Clark25415e22009-10-12 13:41:28 -04004841 if (keepScrollBarsVisible) {
4842 if (mHeldMotionless != MOTIONLESS_TRUE) {
4843 mHeldMotionless = MOTIONLESS_TRUE;
4844 invalidate();
4845 }
Grace Kloba5b2c0562009-09-30 10:17:13 -07004846 // keep the scrollbar on the screen even there is no scroll
4847 awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
4848 false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004849 // return false to indicate that we can't pan out of the
4850 // view space
Cary Clark25415e22009-10-12 13:41:28 -04004851 return !done;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004852 }
4853 break;
4854 }
4855 case MotionEvent.ACTION_UP: {
Grace Klobac2242f22010-03-05 14:00:26 -08004856 // pass the touch events from UI thread to WebCore thread
4857 if (shouldForwardTouchEvent()) {
4858 TouchEventData ted = new TouchEventData();
4859 ted.mAction = action;
4860 ted.mX = contentX;
4861 ted.mY = contentY;
4862 ted.mMetaState = ev.getMetaState();
4863 ted.mReprocess = mDeferTouchProcess;
4864 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Mike Reed19f3f0e2009-11-12 12:50:20 -05004865 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004866 mLastTouchUpTime = eventTime;
4867 switch (mTouchMode) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004868 case TOUCH_DOUBLE_TAP_MODE: // double tap
4869 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobac2242f22010-03-05 14:00:26 -08004870 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
4871 if (inFullScreenMode() || mDeferTouchProcess) {
4872 TouchEventData ted = new TouchEventData();
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004873 ted.mAction = WebViewCore.ACTION_DOUBLETAP;
Grace Klobac2242f22010-03-05 14:00:26 -08004874 ted.mX = contentX;
4875 ted.mY = contentY;
Ben Murdoch8a032a32010-02-02 18:20:11 +00004876 ted.mMetaState = ev.getMetaState();
Grace Klobac2242f22010-03-05 14:00:26 -08004877 ted.mReprocess = mDeferTouchProcess;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004878 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Grace Klobac2242f22010-03-05 14:00:26 -08004879 } else if (mPreventDefault != PREVENT_DEFAULT_YES){
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004880 doDoubleTap();
Grace Klobac2242f22010-03-05 14:00:26 -08004881 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08004882 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004883 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004884 case TOUCH_SELECT_MODE:
4885 commitCopy();
4886 mTouchSelection = false;
4887 break;
Grace Klobaf58af622009-09-24 17:41:23 -07004888 case TOUCH_INIT_MODE: // tap
Grace Klobad66d84f2009-09-27 14:48:07 -07004889 case TOUCH_SHORTPRESS_START_MODE:
4890 case TOUCH_SHORTPRESS_MODE:
Grace Klobaf58af622009-09-24 17:41:23 -07004891 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad66d84f2009-09-27 14:48:07 -07004892 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Klobac2242f22010-03-05 14:00:26 -08004893 if (mConfirmMove) {
Grace Klobaf58af622009-09-24 17:41:23 -07004894 Log.w(LOGTAG, "Miss a drag as we are waiting for" +
4895 " WebCore's response for touch down.");
Grace Klobac2242f22010-03-05 14:00:26 -08004896 if (mPreventDefault != PREVENT_DEFAULT_YES
Grace Klobad7625dd2010-03-04 11:46:12 -08004897 && (computeMaxScrollX() > 0
4898 || computeMaxScrollY() > 0)) {
Grace Klobac2242f22010-03-05 14:00:26 -08004899 // UI takes control back, cancel WebCore touch
4900 cancelWebCoreTouchEvent(contentX, contentY,
4901 true);
Grace Klobaf58af622009-09-24 17:41:23 -07004902 // we will not rewrite drag code here, but we
4903 // will try fling if it applies.
Grace Klobaa7bc87c2010-01-29 14:56:25 -08004904 WebViewCore.reducePriority();
Grace Klobaf58af622009-09-24 17:41:23 -07004905 // fall through to TOUCH_DRAG_MODE
4906 } else {
4907 break;
4908 }
4909 } else {
Grace Klobac2242f22010-03-05 14:00:26 -08004910 if (mTouchMode == TOUCH_INIT_MODE) {
4911 mPrivateHandler.sendEmptyMessageDelayed(
4912 RELEASE_SINGLE_TAP, ViewConfiguration
4913 .getDoubleTapTimeout());
4914 } else {
4915 doShortPress();
Grace Klobaf58af622009-09-24 17:41:23 -07004916 }
4917 break;
4918 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004919 case TOUCH_DRAG_MODE:
Cary Clark25415e22009-10-12 13:41:28 -04004920 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4921 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
4922 mHeldMotionless = MOTIONLESS_TRUE;
Mike Reeddf4cf292009-09-15 14:31:54 -04004923 // redraw in high-quality, as we're done dragging
4924 invalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004925 // if the user waits a while w/o moving before the
4926 // up, we don't want to do a fling
4927 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
Grace Klobac2242f22010-03-05 14:00:26 -08004928 if (mVelocityTracker == null) {
4929 Log.e(LOGTAG, "Got null mVelocityTracker when "
4930 + "mPreventDefault = "
4931 + mPreventDefault
4932 + " mDeferTouchProcess = "
4933 + mDeferTouchProcess);
4934 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004935 mVelocityTracker.addMovement(ev);
4936 doFling();
4937 break;
Grace Klobad7625dd2010-03-04 11:46:12 -08004938 } else {
4939 if (mScroller.springback(mScrollX, mScrollY, 0,
4940 computeMaxScrollX(), 0,
4941 computeMaxScrollY())) {
4942 invalidate();
4943 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004944 }
Cary Clark278ce052009-08-31 16:08:42 -04004945 mLastVelocity = 0;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08004946 WebViewCore.resumePriority();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004947 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004948 }
Grace Klobac2242f22010-03-05 14:00:26 -08004949 stopTouch();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004950 break;
4951 }
4952 case MotionEvent.ACTION_CANCEL: {
Grace Klobad7625dd2010-03-04 11:46:12 -08004953 if (mTouchMode == TOUCH_DRAG_MODE) {
Grace Klobac2242f22010-03-05 14:00:26 -08004954 mScroller.springback(mScrollX, mScrollY, 0,
4955 computeMaxScrollX(), 0, computeMaxScrollY());
4956 invalidate();
Grace Klobad7625dd2010-03-04 11:46:12 -08004957 }
Grace Klobac2242f22010-03-05 14:00:26 -08004958 cancelWebCoreTouchEvent(contentX, contentY, false);
4959 cancelTouch();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004960 break;
4961 }
4962 }
4963 return true;
4964 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004965
Grace Klobac2242f22010-03-05 14:00:26 -08004966 private void cancelWebCoreTouchEvent(int x, int y, boolean removeEvents) {
4967 if (shouldForwardTouchEvent()) {
4968 if (removeEvents) {
4969 mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
4970 }
4971 TouchEventData ted = new TouchEventData();
4972 ted.mX = x;
4973 ted.mY = y;
4974 ted.mAction = MotionEvent.ACTION_CANCEL;
4975 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
4976 mPreventDefault = PREVENT_DEFAULT_IGNORE;
4977 }
4978 }
4979
Grace Kloba3a0def22010-01-23 21:11:54 -08004980 private void startTouch(float x, float y, long eventTime) {
4981 // Remember where the motion event started
4982 mLastTouchX = x;
4983 mLastTouchY = y;
4984 mLastTouchTime = eventTime;
4985 mVelocityTracker = VelocityTracker.obtain();
4986 mSnapScrollMode = SNAP_NONE;
4987 if (mDragTracker != null) {
4988 mDragTrackerHandler = new DragTrackerHandler(x, y, mDragTracker);
4989 }
4990 }
4991
Grace Klobac2242f22010-03-05 14:00:26 -08004992 private void startDrag() {
4993 WebViewCore.reducePriority();
4994 if (!mDragFromTextInput) {
4995 nativeHideCursor();
4996 }
4997 WebSettings settings = getSettings();
4998 if (settings.supportZoom()
4999 && settings.getBuiltInZoomControls()
5000 && !mZoomButtonsController.isVisible()
5001 && mMinZoomScale < mMaxZoomScale) {
5002 mZoomButtonsController.setVisible(true);
5003 int count = settings.getDoubleTapToastCount();
5004 if (mInZoomOverview && count > 0) {
5005 settings.setDoubleTapToastCount(--count);
5006 Toast.makeText(mContext,
5007 com.android.internal.R.string.double_tap_toast,
5008 Toast.LENGTH_LONG).show();
5009 }
5010 }
5011 }
5012
5013 private void doDrag(int deltaX, int deltaY) {
5014 if ((deltaX | deltaY) != 0) {
5015 overscrollBy(deltaX, deltaY, mScrollX, mScrollY,
5016 computeMaxScrollX(), computeMaxScrollY(),
Adam Powell8a836a82010-03-17 20:17:04 -07005017 getViewWidth() / 3, getViewHeight() / 3, true);
Grace Klobac2242f22010-03-05 14:00:26 -08005018 }
5019 if (!getSettings().getBuiltInZoomControls()) {
5020 boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
5021 if (mZoomControls != null && showPlusMinus) {
5022 if (mZoomControls.getVisibility() == View.VISIBLE) {
5023 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5024 } else {
5025 mZoomControls.show(showPlusMinus, false);
5026 }
5027 mPrivateHandler.postDelayed(mZoomControlRunnable,
5028 ZOOM_CONTROLS_TIMEOUT);
5029 }
5030 }
5031 }
5032
5033 private void stopTouch() {
5034 if (mDragTrackerHandler != null) {
5035 mDragTrackerHandler.stopDrag();
5036 }
5037 // we also use mVelocityTracker == null to tell us that we are
5038 // not "moving around", so we can take the slower/prettier
5039 // mode in the drawing code
5040 if (mVelocityTracker != null) {
5041 mVelocityTracker.recycle();
5042 mVelocityTracker = null;
5043 }
5044 }
5045
Grace Kloba3a0def22010-01-23 21:11:54 -08005046 private void cancelTouch() {
5047 if (mDragTrackerHandler != null) {
5048 mDragTrackerHandler.stopDrag();
Grace Kloba3a0def22010-01-23 21:11:54 -08005049 }
5050 // we also use mVelocityTracker == null to tell us that we are
5051 // not "moving around", so we can take the slower/prettier
5052 // mode in the drawing code
5053 if (mVelocityTracker != null) {
5054 mVelocityTracker.recycle();
5055 mVelocityTracker = null;
5056 }
5057 if (mTouchMode == TOUCH_DRAG_MODE) {
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005058 WebViewCore.resumePriority();
Grace Kloba3a0def22010-01-23 21:11:54 -08005059 }
5060 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
5061 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
5062 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
5063 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
5064 mHeldMotionless = MOTIONLESS_TRUE;
5065 mTouchMode = TOUCH_DONE_MODE;
5066 nativeHideCursor();
5067 }
5068
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005069 private long mTrackballFirstTime = 0;
5070 private long mTrackballLastTime = 0;
5071 private float mTrackballRemainsX = 0.0f;
5072 private float mTrackballRemainsY = 0.0f;
5073 private int mTrackballXMove = 0;
5074 private int mTrackballYMove = 0;
5075 private boolean mExtendSelection = false;
5076 private boolean mTouchSelection = false;
5077 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
5078 private static final int TRACKBALL_TIMEOUT = 200;
5079 private static final int TRACKBALL_WAIT = 100;
5080 private static final int TRACKBALL_SCALE = 400;
5081 private static final int TRACKBALL_SCROLL_COUNT = 5;
5082 private static final int TRACKBALL_MOVE_COUNT = 10;
5083 private static final int TRACKBALL_MULTIPLIER = 3;
5084 private static final int SELECT_CURSOR_OFFSET = 16;
5085 private int mSelectX = 0;
5086 private int mSelectY = 0;
Cary Clark5da9aeb2009-10-06 17:40:53 -04005087 private boolean mFocusSizeChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005088 private boolean mShiftIsPressed = false;
5089 private boolean mTrackballDown = false;
5090 private long mTrackballUpTime = 0;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005091 private long mLastCursorTime = 0;
5092 private Rect mLastCursorBounds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005093
5094 // Set by default; BrowserActivity clears to interpret trackball data
Cary Clarkd6982c92009-05-29 11:02:22 -04005095 // directly for movement. Currently, the framework only passes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005096 // arrow key events, not trackball events, from one child to the next
5097 private boolean mMapTrackballToArrowKeys = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04005098
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005099 public void setMapTrackballToArrowKeys(boolean setMap) {
5100 mMapTrackballToArrowKeys = setMap;
5101 }
5102
5103 void resetTrackballTime() {
5104 mTrackballLastTime = 0;
5105 }
5106
5107 @Override
5108 public boolean onTrackballEvent(MotionEvent ev) {
5109 long time = ev.getEventTime();
5110 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
5111 if (ev.getY() > 0) pageDown(true);
5112 if (ev.getY() < 0) pageUp(true);
5113 return true;
5114 }
Cary Clarka91e9722010-03-12 09:13:53 -05005115 boolean shiftPressed = mShiftIsPressed && (mNativeClass == 0
5116 || !nativeFocusIsPlugin());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005117 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Cary Clarka91e9722010-03-12 09:13:53 -05005118 if (shiftPressed) {
Cary Clarkbadd8392009-10-15 13:32:08 -04005119 return true; // discard press if copy in progress
5120 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005121 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04005122 if (mNativeClass == 0) {
5123 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005124 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04005125 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005126 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
5127 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
5128 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005129 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005130 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005131 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04005132 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005133 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005134 }
5135 if (isInTouchMode()) requestFocusFromTouch();
5136 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04005137 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005138 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04005139 // LONG_PRESS_CENTER is set in common onKeyDown
5140 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005141 mTrackballDown = false;
5142 mTrackballUpTime = time;
Cary Clarka91e9722010-03-12 09:13:53 -05005143 if (shiftPressed) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005144 if (mExtendSelection) {
5145 commitCopy();
5146 } else {
5147 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04005148 invalidate(); // draw the i-beam instead of the arrow
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005149 }
Cary Clarkbadd8392009-10-15 13:32:08 -04005150 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005151 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005152 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005153 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04005154 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005155 );
5156 }
5157 return false; // let common code in onKeyUp at it
5158 }
5159 if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005160 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005161 return false;
5162 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005163 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005164 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005165 return true; // discard move if trackball is down
5166 }
5167 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005168 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005169 return true;
5170 }
5171 // TODO: alternatively we can do panning as touch does
5172 switchOutDrawHistory();
5173 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005174 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005175 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005176 + time + " last=" + mTrackballLastTime);
5177 }
5178 mTrackballFirstTime = time;
5179 mTrackballXMove = mTrackballYMove = 0;
5180 }
5181 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005182 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005183 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
5184 }
5185 mTrackballRemainsX += ev.getX();
5186 mTrackballRemainsY += ev.getY();
5187 doTrackball(time);
5188 return true;
5189 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005190
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005191 void moveSelection(float xRate, float yRate) {
5192 if (mNativeClass == 0)
5193 return;
5194 int width = getViewWidth();
5195 int height = getViewHeight();
Cary Clarkc05af372009-10-16 10:52:27 -04005196 mSelectX += xRate;
5197 mSelectY += yRate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005198 int maxX = width + mScrollX;
5199 int maxY = height + mScrollY;
5200 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
5201 , mSelectX));
5202 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
5203 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005204 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005205 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005206 + " mSelectX=" + mSelectX
5207 + " mSelectY=" + mSelectY
5208 + " mScrollX=" + mScrollX
5209 + " mScrollY=" + mScrollY
5210 + " xRate=" + xRate
5211 + " yRate=" + yRate
5212 );
5213 }
Leon Scroggins0236e672009-09-02 21:12:08 -04005214 nativeMoveSelection(viewToContentX(mSelectX),
5215 viewToContentY(mSelectY), mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005216 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04005217 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005218 : 0;
5219 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04005220 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005221 : 0;
5222 pinScrollBy(scrollX, scrollY, true, 0);
5223 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
5224 requestRectangleOnScreen(select);
5225 invalidate();
5226 }
5227
5228 private int scaleTrackballX(float xRate, int width) {
5229 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
5230 int nextXMove = xMove;
5231 if (xMove > 0) {
5232 if (xMove > mTrackballXMove) {
5233 xMove -= mTrackballXMove;
5234 }
5235 } else if (xMove < mTrackballXMove) {
5236 xMove -= mTrackballXMove;
5237 }
5238 mTrackballXMove = nextXMove;
5239 return xMove;
5240 }
5241
5242 private int scaleTrackballY(float yRate, int height) {
5243 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
5244 int nextYMove = yMove;
5245 if (yMove > 0) {
5246 if (yMove > mTrackballYMove) {
5247 yMove -= mTrackballYMove;
5248 }
5249 } else if (yMove < mTrackballYMove) {
5250 yMove -= mTrackballYMove;
5251 }
5252 mTrackballYMove = nextYMove;
5253 return yMove;
5254 }
5255
5256 private int keyCodeToSoundsEffect(int keyCode) {
5257 switch(keyCode) {
5258 case KeyEvent.KEYCODE_DPAD_UP:
5259 return SoundEffectConstants.NAVIGATION_UP;
5260 case KeyEvent.KEYCODE_DPAD_RIGHT:
5261 return SoundEffectConstants.NAVIGATION_RIGHT;
5262 case KeyEvent.KEYCODE_DPAD_DOWN:
5263 return SoundEffectConstants.NAVIGATION_DOWN;
5264 case KeyEvent.KEYCODE_DPAD_LEFT:
5265 return SoundEffectConstants.NAVIGATION_LEFT;
5266 }
5267 throw new IllegalArgumentException("keyCode must be one of " +
5268 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
5269 "KEYCODE_DPAD_LEFT}.");
5270 }
5271
5272 private void doTrackball(long time) {
5273 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
5274 if (elapsed == 0) {
5275 elapsed = TRACKBALL_TIMEOUT;
5276 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005277 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005278 float yRate = mTrackballRemainsY * 1000 / elapsed;
Cary Clarkc05af372009-10-16 10:52:27 -04005279 int viewWidth = getViewWidth();
5280 int viewHeight = getViewHeight();
Cary Clarka91e9722010-03-12 09:13:53 -05005281 if (mShiftIsPressed && (mNativeClass == 0 || !nativeFocusIsPlugin())) {
Cary Clarkc05af372009-10-16 10:52:27 -04005282 moveSelection(scaleTrackballX(xRate, viewWidth),
5283 scaleTrackballY(yRate, viewHeight));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005284 mTrackballRemainsX = mTrackballRemainsY = 0;
5285 return;
5286 }
5287 float ax = Math.abs(xRate);
5288 float ay = Math.abs(yRate);
5289 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005290 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005291 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
5292 + " xRate=" + xRate
5293 + " yRate=" + yRate
5294 + " mTrackballRemainsX=" + mTrackballRemainsX
5295 + " mTrackballRemainsY=" + mTrackballRemainsY);
5296 }
Cary Clarkc05af372009-10-16 10:52:27 -04005297 int width = mContentWidth - viewWidth;
5298 int height = mContentHeight - viewHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005299 if (width < 0) width = 0;
5300 if (height < 0) height = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005301 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
5302 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
5303 maxA = Math.max(ax, ay);
5304 int count = Math.max(0, (int) maxA);
5305 int oldScrollX = mScrollX;
5306 int oldScrollY = mScrollY;
5307 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005308 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
5309 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005310 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
5311 KeyEvent.KEYCODE_DPAD_RIGHT;
5312 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005313 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005314 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005315 + " count=" + count
5316 + " mTrackballRemainsX=" + mTrackballRemainsX
5317 + " mTrackballRemainsY=" + mTrackballRemainsY);
5318 }
Cary Clarka91e9722010-03-12 09:13:53 -05005319 if (mNativeClass != 0 && nativeFocusIsPlugin()) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005320 for (int i = 0; i < count; i++) {
5321 letPluginHandleNavKey(selectKeyCode, time, true);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005322 }
Leon Scroggins3dc02fe2010-03-09 11:27:34 -05005323 letPluginHandleNavKey(selectKeyCode, time, false);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005324 } else if (navHandledKey(selectKeyCode, count, false, time)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005325 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
5326 }
5327 mTrackballRemainsX = mTrackballRemainsY = 0;
5328 }
5329 if (count >= TRACKBALL_SCROLL_COUNT) {
5330 int xMove = scaleTrackballX(xRate, width);
5331 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005332 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005333 Log.v(LOGTAG, "doTrackball pinScrollBy"
5334 + " count=" + count
5335 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04005336 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
5337 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005338 );
5339 }
5340 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
5341 xMove = 0;
5342 }
5343 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
5344 yMove = 0;
5345 }
5346 if (xMove != 0 || yMove != 0) {
5347 pinScrollBy(xMove, yMove, true, 0);
5348 }
5349 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04005350 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005351 }
5352
Grace Klobad7625dd2010-03-04 11:46:12 -08005353 private int computeMaxScrollX() {
5354 return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
5355 }
5356
Mike Reede8853fc2009-09-04 14:01:48 -04005357 private int computeMaxScrollY() {
Grace Klobad7625dd2010-03-04 11:46:12 -08005358 return Math.max(computeVerticalScrollRange() + getTitleHeight()
Mike Reede5e63f42010-03-19 14:38:23 -04005359 - getViewHeightWithTitle(), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04005360 }
5361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005362 public void flingScroll(int vx, int vy) {
Grace Klobad7625dd2010-03-04 11:46:12 -08005363 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
5364 computeMaxScrollY(), getViewWidth() / 3, getViewHeight() / 3);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005365 invalidate();
5366 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005367
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005368 private void doFling() {
5369 if (mVelocityTracker == null) {
5370 return;
5371 }
Grace Klobad7625dd2010-03-04 11:46:12 -08005372 int maxX = computeMaxScrollX();
Mike Reede8853fc2009-09-04 14:01:48 -04005373 int maxY = computeMaxScrollY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005374
Romain Guy4296fc42009-07-06 11:48:52 -07005375 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005376 int vx = (int) mVelocityTracker.getXVelocity();
5377 int vy = (int) mVelocityTracker.getYVelocity();
5378
5379 if (mSnapScrollMode != SNAP_NONE) {
Cary Clarkac492e12009-10-14 14:53:37 -04005380 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005381 vy = 0;
5382 } else {
5383 vx = 0;
5384 }
5385 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005386 if (true /* EMG release: make our fling more like Maps' */) {
5387 // maps cuts their velocity in half
5388 vx = vx * 3 / 4;
5389 vy = vy * 3 / 4;
5390 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04005391 if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005392 WebViewCore.resumePriority();
Grace Klobad7625dd2010-03-04 11:46:12 -08005393 if (mScroller.springback(mScrollX, mScrollY, 0, computeMaxScrollX(),
5394 0, computeMaxScrollY())) {
5395 invalidate();
5396 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04005397 return;
5398 }
Cary Clark278ce052009-08-31 16:08:42 -04005399 float currentVelocity = mScroller.getCurrVelocity();
5400 if (mLastVelocity > 0 && currentVelocity > 0) {
5401 float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
5402 - Math.atan2(vy, vx)));
5403 final float circle = (float) (Math.PI) * 2.0f;
5404 if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
5405 vx += currentVelocity * mLastVelX / mLastVelocity;
5406 vy += currentVelocity * mLastVelY / mLastVelocity;
5407 if (DebugFlags.WEB_VIEW) {
5408 Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
5409 }
5410 } else if (DebugFlags.WEB_VIEW) {
5411 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
5412 }
5413 } else if (DebugFlags.WEB_VIEW) {
5414 Log.v(LOGTAG, "doFling start last=" + mLastVelocity
Cary Clarkaa7caa62009-09-08 14:15:07 -04005415 + " current=" + currentVelocity
5416 + " vx=" + vx + " vy=" + vy
5417 + " maxX=" + maxX + " maxY=" + maxY
5418 + " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
Cary Clark278ce052009-08-31 16:08:42 -04005419 }
5420 mLastVelX = vx;
5421 mLastVelY = vy;
5422 mLastVelocity = (float) Math.hypot(vx, vy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005423
Grace Klobaee8500e2010-03-19 17:23:38 -07005424 // no horizontal overscroll if the content just fits
Grace Klobad7625dd2010-03-04 11:46:12 -08005425 mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
Grace Klobaee8500e2010-03-19 17:23:38 -07005426 maxX == 0 ? 0 : getViewWidth() / 3, getViewHeight() / 3);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005427 // TODO: duration is calculated based on velocity, if the range is
5428 // small, the animation will stop before duration is up. We may
5429 // want to calculate how long the animation is going to run to precisely
5430 // resume the webcore update.
5431 final int time = mScroller.getDuration();
Grace Kloba96949ef2010-01-25 09:53:01 -08005432 mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_PRIORITY, time);
Mike Cleronf116bf82009-09-27 19:14:12 -07005433 awakenScrollBars(time);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005434 invalidate();
5435 }
5436
Grace Klobac6f95fe2010-03-10 13:25:34 -08005437 private boolean zoomWithPreview(float scale, boolean updateTextWrapScale) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005438 float oldScale = mActualScale;
Grace Kloba675c7d22009-07-23 09:21:21 -07005439 mInitialScrollX = mScrollX;
5440 mInitialScrollY = mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005441
Grace Kloba25737912009-06-19 12:42:47 -07005442 // snap to DEFAULT_SCALE if it is close
Grace Klobac6f95fe2010-03-10 13:25:34 -08005443 if (Math.abs(scale - mDefaultScale) < MINIMUM_SCALE_INCREMENT) {
Grace Kloba0d8b77c2009-06-25 11:20:51 -07005444 scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005445 }
5446
Grace Klobac6f95fe2010-03-10 13:25:34 -08005447 setNewZoomScale(scale, updateTextWrapScale, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005448
5449 if (oldScale != mActualScale) {
5450 // use mZoomPickerScale to see zoom preview first
5451 mZoomStart = SystemClock.uptimeMillis();
5452 mInvInitialZoomScale = 1.0f / oldScale;
5453 mInvFinalZoomScale = 1.0f / mActualScale;
5454 mZoomScale = mActualScale;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005455 WebViewCore.pauseUpdatePicture(mWebViewCore);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005456 invalidate();
5457 return true;
5458 } else {
5459 return false;
5460 }
5461 }
5462
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005463 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005464 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
5465 * in charge of installing this view to the view hierarchy. This view will
5466 * become visible when the user starts scrolling via touch and fade away if
5467 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005468 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07005469 * API version 3 introduces a built-in zoom mechanism that is shown
5470 * automatically by the MapView. This is the preferred approach for
5471 * showing the zoom UI.
5472 *
5473 * @deprecated The built-in zoom mechanism is preferred, see
5474 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005475 */
The Android Open Source Project10592532009-03-18 17:39:46 -07005476 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005477 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07005478 if (!getSettings().supportZoom()) {
5479 Log.w(LOGTAG, "This WebView doesn't support zoom.");
5480 return null;
5481 }
5482 if (mZoomControls == null) {
5483 mZoomControls = createZoomControls();
Cary Clarkd6982c92009-05-29 11:02:22 -04005484
The Android Open Source Project10592532009-03-18 17:39:46 -07005485 /*
5486 * need to be set to VISIBLE first so that getMeasuredHeight() in
5487 * {@link #onSizeChanged()} can return the measured value for proper
5488 * layout.
5489 */
5490 mZoomControls.setVisibility(View.VISIBLE);
5491 mZoomControlRunnable = new Runnable() {
5492 public void run() {
Cary Clarkd6982c92009-05-29 11:02:22 -04005493
The Android Open Source Project10592532009-03-18 17:39:46 -07005494 /* Don't dismiss the controls if the user has
5495 * focus on them. Wait and check again later.
5496 */
5497 if (!mZoomControls.hasFocus()) {
5498 mZoomControls.hide();
5499 } else {
5500 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5501 mPrivateHandler.postDelayed(mZoomControlRunnable,
5502 ZOOM_CONTROLS_TIMEOUT);
5503 }
5504 }
5505 };
5506 }
5507 return mZoomControls;
5508 }
5509
5510 private ExtendedZoomControls createZoomControls() {
5511 ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
5512 , null);
5513 zoomControls.setOnZoomInClickListener(new OnClickListener() {
5514 public void onClick(View v) {
5515 // reset time out
5516 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5517 mPrivateHandler.postDelayed(mZoomControlRunnable,
5518 ZOOM_CONTROLS_TIMEOUT);
5519 zoomIn();
5520 }
5521 });
5522 zoomControls.setOnZoomOutClickListener(new OnClickListener() {
5523 public void onClick(View v) {
5524 // reset time out
5525 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5526 mPrivateHandler.postDelayed(mZoomControlRunnable,
5527 ZOOM_CONTROLS_TIMEOUT);
5528 zoomOut();
5529 }
5530 });
The Android Open Source Project10592532009-03-18 17:39:46 -07005531 return zoomControls;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005532 }
5533
5534 /**
5535 * Gets the {@link ZoomButtonsController} which can be used to add
5536 * additional buttons to the zoom controls window.
Cary Clarkd6982c92009-05-29 11:02:22 -04005537 *
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005538 * @return The instance of {@link ZoomButtonsController} used by this class,
5539 * or null if it is unavailable.
The Android Open Source Project10592532009-03-18 17:39:46 -07005540 * @hide
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005541 */
5542 public ZoomButtonsController getZoomButtonsController() {
5543 return mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005544 }
5545
5546 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005547 * Perform zoom in in the webview
5548 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
5549 */
5550 public boolean zoomIn() {
5551 // TODO: alternatively we can disallow this during draw history mode
5552 switchOutDrawHistory();
Grace Kloba3a0def22010-01-23 21:11:54 -08005553 mInZoomOverview = false;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005554 // Center zooming to the center of the screen.
Grace Kloba3a0def22010-01-23 21:11:54 -08005555 mZoomCenterX = getViewWidth() * .5f;
5556 mZoomCenterY = getViewHeight() * .5f;
5557 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5558 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005559 return zoomWithPreview(mActualScale * 1.25f, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005560 }
5561
5562 /**
5563 * Perform zoom out in the webview
5564 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
5565 */
5566 public boolean zoomOut() {
5567 // TODO: alternatively we can disallow this during draw history mode
5568 switchOutDrawHistory();
Grace Kloba3a0def22010-01-23 21:11:54 -08005569 // Center zooming to the center of the screen.
5570 mZoomCenterX = getViewWidth() * .5f;
5571 mZoomCenterY = getViewHeight() * .5f;
5572 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5573 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005574 return zoomWithPreview(mActualScale * 0.8f, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005575 }
5576
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005577 private void updateSelection() {
5578 if (mNativeClass == 0) {
5579 return;
5580 }
5581 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04005582 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
5583 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005584 Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
5585 contentX + mNavSlop, contentY + mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005586 nativeSelectBestAt(rect);
5587 }
5588
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005589 /**
Leon Scroggins72543e12009-07-23 15:29:45 -04005590 * Scroll the focused text field/area to match the WebTextView
Cary Clarkeaa18de2009-09-28 12:50:42 -04005591 * @param xPercent New x position of the WebTextView from 0 to 1.
Leon Scroggins72543e12009-07-23 15:29:45 -04005592 * @param y New y position of the WebTextView in view coordinates
5593 */
Cary Clarkeaa18de2009-09-28 12:50:42 -04005594 /*package*/ void scrollFocusedTextInput(float xPercent, int y) {
Leon Scroggins72543e12009-07-23 15:29:45 -04005595 if (!inEditingMode() || mWebViewCore == null) {
5596 return;
5597 }
Cary Clarkeaa18de2009-09-28 12:50:42 -04005598 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT,
Leon Scrogginsd3997e52009-09-21 14:15:18 -04005599 // Since this position is relative to the top of the text input
5600 // field, we do not need to take the title bar's height into
5601 // consideration.
Cary Clarkeaa18de2009-09-28 12:50:42 -04005602 viewToContentDimension(y),
5603 new Float(xPercent));
Leon Scroggins72543e12009-07-23 15:29:45 -04005604 }
5605
5606 /**
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005607 * Set our starting point and time for a drag from the WebTextView.
5608 */
5609 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
5610 if (!inEditingMode()) {
5611 return;
5612 }
5613 mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
5614 mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
5615 mLastTouchTime = eventTime;
5616 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04005617 abortAnimation();
Grace Kloba96949ef2010-01-25 09:53:01 -08005618 mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005619 }
5620 mSnapScrollMode = SNAP_NONE;
5621 mVelocityTracker = VelocityTracker.obtain();
5622 mTouchMode = TOUCH_DRAG_START_MODE;
5623 }
5624
5625 /**
5626 * Given a motion event from the WebTextView, set its location to our
5627 * coordinates, and handle the event.
5628 */
5629 /*package*/ boolean textFieldDrag(MotionEvent event) {
5630 if (!inEditingMode()) {
5631 return false;
5632 }
Leon Scroggins72543e12009-07-23 15:29:45 -04005633 mDragFromTextInput = true;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005634 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
5635 (float) (mWebTextView.getTop() - mScrollY));
Leon Scroggins72543e12009-07-23 15:29:45 -04005636 boolean result = onTouchEvent(event);
5637 mDragFromTextInput = false;
5638 return result;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005639 }
5640
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005641 /**
Leon Scrogginsf90b1262009-11-24 14:49:21 -05005642 * Due a touch up from a WebTextView. This will be handled by webkit to
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005643 * change the selection.
5644 * @param event MotionEvent in the WebTextView's coordinates.
5645 */
5646 /*package*/ void touchUpOnTextField(MotionEvent event) {
5647 if (!inEditingMode()) {
5648 return;
5649 }
Leon Scroggins0236e672009-09-02 21:12:08 -04005650 int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
5651 int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
Leon Scrogginsf90b1262009-11-24 14:49:21 -05005652 nativeMotionUp(x, y, mNavSlop);
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005653 }
5654
Leon Scroggins1d96ca02009-10-23 11:49:03 -04005655 /**
5656 * Called when pressing the center key or trackball on a textfield.
5657 */
5658 /*package*/ void centerKeyPressOnTextField() {
5659 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
5660 nativeCursorNodePointer());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005661 }
5662
5663 private void doShortPress() {
5664 if (mNativeClass == 0) {
5665 return;
5666 }
Grace Klobac2242f22010-03-05 14:00:26 -08005667 if (mPreventDefault == PREVENT_DEFAULT_YES) {
5668 return;
5669 }
5670 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005671 switchOutDrawHistory();
5672 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04005673 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
5674 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
Cary Clark1cb97ee2009-12-11 12:10:36 -05005675 if (nativePointInNavCache(contentX, contentY, mNavSlop)) {
5676 WebViewCore.MotionUpData motionUpData = new WebViewCore
5677 .MotionUpData();
5678 motionUpData.mFrame = nativeCacheHitFramePointer();
5679 motionUpData.mNode = nativeCacheHitNodePointer();
5680 motionUpData.mBounds = nativeCacheHitNodeBounds();
5681 motionUpData.mX = contentX;
5682 motionUpData.mY = contentY;
5683 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
5684 motionUpData);
5685 } else {
Cary Clarkbad0c542010-01-11 14:58:21 -05005686 doMotionUp(contentX, contentY);
Cary Clark1cb97ee2009-12-11 12:10:36 -05005687 }
5688 }
5689
Cary Clarkbad0c542010-01-11 14:58:21 -05005690 private void doMotionUp(int contentX, int contentY) {
Dan Egnor18e93962010-02-10 19:27:58 -08005691 if (mLogEvent && nativeMotionUp(contentX, contentY, mNavSlop)) {
5692 EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005693 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005694 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005695 playSoundEffect(SoundEffectConstants.CLICK);
5696 }
5697 }
5698
Grace Klobac6f95fe2010-03-10 13:25:34 -08005699 /*
5700 * Return true if the view (Plugin) is fully visible and maximized inside
5701 * the WebView.
5702 */
5703 private boolean isPluginFitOnScreen(ViewManager.ChildView view) {
5704 int viewWidth = getViewWidth();
5705 int viewHeight = getViewHeightWithTitle();
5706 float scale = Math.min((float) viewWidth / view.width,
5707 (float) viewHeight / view.height);
5708 if (scale < mMinZoomScale) {
5709 scale = mMinZoomScale;
5710 } else if (scale > mMaxZoomScale) {
5711 scale = mMaxZoomScale;
5712 }
5713 if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) {
5714 if (contentToViewX(view.x) >= mScrollX
5715 && contentToViewX(view.x + view.width) <= mScrollX
5716 + viewWidth
5717 && contentToViewY(view.y) >= mScrollY
5718 && contentToViewY(view.y + view.height) <= mScrollY
5719 + viewHeight) {
5720 return true;
5721 }
5722 }
5723 return false;
5724 }
5725
5726 /*
Grace Klobae8300a12010-03-12 13:32:55 -08005727 * Maximize and center the rectangle, specified in the document coordinate
5728 * space, inside the WebView. If the zoom doesn't need to be changed, do an
5729 * animated scroll to center it. If the zoom needs to be changed, find the
5730 * zoom center and do a smooth zoom transition.
Grace Klobac6f95fe2010-03-10 13:25:34 -08005731 */
Grace Klobae8300a12010-03-12 13:32:55 -08005732 private void centerFitRect(int docX, int docY, int docWidth, int docHeight) {
Grace Klobac6f95fe2010-03-10 13:25:34 -08005733 int viewWidth = getViewWidth();
5734 int viewHeight = getViewHeightWithTitle();
Grace Klobae8300a12010-03-12 13:32:55 -08005735 float scale = Math.min((float) viewWidth / docWidth, (float) viewHeight
5736 / docHeight);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005737 if (scale < mMinZoomScale) {
5738 scale = mMinZoomScale;
5739 } else if (scale > mMaxZoomScale) {
5740 scale = mMaxZoomScale;
5741 }
5742 if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) {
Grace Klobae8300a12010-03-12 13:32:55 -08005743 pinScrollTo(contentToViewX(docX + docWidth / 2) - viewWidth / 2,
5744 contentToViewY(docY + docHeight / 2) - viewHeight / 2,
Grace Klobac6f95fe2010-03-10 13:25:34 -08005745 true, 0);
5746 } else {
Grace Kloba1e65d9e2010-03-12 19:19:48 -08005747 float oldScreenX = docX * mActualScale - mScrollX;
5748 float rectViewX = docX * scale;
5749 float rectViewWidth = docWidth * scale;
5750 float newMaxWidth = mContentWidth * scale;
5751 float newScreenX = (viewWidth - rectViewWidth) / 2;
Grace Klobac6f95fe2010-03-10 13:25:34 -08005752 // pin the newX to the WebView
Grace Klobae8300a12010-03-12 13:32:55 -08005753 if (newScreenX > rectViewX) {
5754 newScreenX = rectViewX;
5755 } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
5756 newScreenX = viewWidth - (newMaxWidth - rectViewX);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005757 }
5758 mZoomCenterX = (oldScreenX * scale - newScreenX * mActualScale)
5759 / (scale - mActualScale);
Grace Kloba1e65d9e2010-03-12 19:19:48 -08005760 float oldScreenY = docY * mActualScale + getTitleHeight()
5761 - mScrollY;
5762 float rectViewY = docY * scale + getTitleHeight();
5763 float rectViewHeight = docHeight * scale;
5764 float newMaxHeight = mContentHeight * scale + getTitleHeight();
5765 float newScreenY = (viewHeight - rectViewHeight) / 2;
Grace Klobac6f95fe2010-03-10 13:25:34 -08005766 // pin the newY to the WebView
Grace Klobae8300a12010-03-12 13:32:55 -08005767 if (newScreenY > rectViewY) {
5768 newScreenY = rectViewY;
5769 } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
5770 newScreenY = viewHeight - (newMaxHeight - rectViewY);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005771 }
5772 mZoomCenterY = (oldScreenY * scale - newScreenY * mActualScale)
5773 / (scale - mActualScale);
5774 zoomWithPreview(scale, false);
5775 }
5776 }
5777
Grace Kloba21817f62010-03-19 18:28:33 -07005778 void dismissZoomControl() {
Grace Klobacad9ab62010-03-21 14:05:14 -07005779 if (mWebViewCore == null) {
5780 // maybe called after WebView's destroy(). As we can't get settings,
5781 // just hide zoom control for both styles.
5782 mZoomButtonsController.setVisible(false);
5783 if (mZoomControls != null) {
5784 mZoomControls.hide();
5785 }
5786 return;
5787 }
Grace Kloba21817f62010-03-19 18:28:33 -07005788 WebSettings settings = getSettings();
5789 if (settings.getBuiltInZoomControls()) {
5790 if (mZoomButtonsController.isVisible()) {
5791 mZoomButtonsController.setVisible(false);
5792 }
5793 } else {
5794 if (mZoomControlRunnable != null) {
5795 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5796 }
5797 if (mZoomControls != null) {
5798 mZoomControls.hide();
5799 }
5800 }
5801 }
5802
Grace Kloba3a0def22010-01-23 21:11:54 -08005803 // Rule for double tap:
5804 // 1. if the current scale is not same as the text wrap scale and layout
5805 // algorithm is NARROW_COLUMNS, fit to column;
5806 // 2. if the current state is not overview mode, change to overview mode;
5807 // 3. if the current state is overview mode, change to default scale.
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005808 private void doDoubleTap() {
Leon Scroggins0236e672009-09-02 21:12:08 -04005809 if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005810 return;
5811 }
5812 mZoomCenterX = mLastTouchX;
5813 mZoomCenterY = mLastTouchY;
Grace Kloba3a0def22010-01-23 21:11:54 -08005814 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5815 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Grace Klobaf8d8b462009-09-20 15:57:49 -07005816 WebSettings settings = getSettings();
Grace Klobaf8d8b462009-09-20 15:57:49 -07005817 settings.setDoubleTapToastCount(0);
Grace Kloba21817f62010-03-19 18:28:33 -07005818 // remove the zoom control after double tap
5819 dismissZoomControl();
Grace Klobac6f95fe2010-03-10 13:25:34 -08005820 ViewManager.ChildView plugin = mViewManager.hitTest(mAnchorX, mAnchorY);
5821 if (plugin != null) {
5822 if (isPluginFitOnScreen(plugin)) {
5823 mInZoomOverview = true;
5824 // Force the titlebar fully reveal in overview mode
5825 if (mScrollY < getTitleHeight()) mScrollY = 0;
5826 zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth,
5827 true);
5828 } else {
5829 mInZoomOverview = false;
Grace Klobae8300a12010-03-12 13:32:55 -08005830 centerFitRect(plugin.x, plugin.y, plugin.width, plugin.height);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005831 }
5832 return;
5833 }
Grace Kloba3a0def22010-01-23 21:11:54 -08005834 boolean zoomToDefault = false;
5835 if ((settings.getLayoutAlgorithm() == WebSettings.LayoutAlgorithm.NARROW_COLUMNS)
Grace Klobac6f95fe2010-03-10 13:25:34 -08005836 && (Math.abs(mActualScale - mTextWrapScale) >= MINIMUM_SCALE_INCREMENT)) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005837 setNewZoomScale(mActualScale, true, true);
5838 float overviewScale = (float) getViewWidth() / mZoomOverviewWidth;
Grace Klobac6f95fe2010-03-10 13:25:34 -08005839 if (Math.abs(mActualScale - overviewScale) < MINIMUM_SCALE_INCREMENT) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005840 mInZoomOverview = true;
5841 }
5842 } else if (!mInZoomOverview) {
Grace Kloba86773fb2009-11-19 11:25:20 -08005843 float newScale = (float) getViewWidth() / mZoomOverviewWidth;
Grace Klobac6f95fe2010-03-10 13:25:34 -08005844 if (Math.abs(mActualScale - newScale) >= MINIMUM_SCALE_INCREMENT) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005845 mInZoomOverview = true;
Grace Kloba86773fb2009-11-19 11:25:20 -08005846 // Force the titlebar fully reveal in overview mode
5847 if (mScrollY < getTitleHeight()) mScrollY = 0;
Grace Klobac6f95fe2010-03-10 13:25:34 -08005848 zoomWithPreview(newScale, true);
5849 } else if (Math.abs(mActualScale - mDefaultScale) >= MINIMUM_SCALE_INCREMENT) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005850 zoomToDefault = true;
Grace Kloba86773fb2009-11-19 11:25:20 -08005851 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005852 } else {
Grace Kloba3a0def22010-01-23 21:11:54 -08005853 zoomToDefault = true;
5854 }
5855 if (zoomToDefault) {
5856 mInZoomOverview = false;
5857 int left = nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005858 if (left != NO_LEFTEDGE) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005859 // add a 5pt padding to the left edge.
5860 int viewLeft = contentToViewX(left < 5 ? 0 : (left - 5))
5861 - mScrollX;
5862 // Re-calculate the zoom center so that the new scroll x will be
5863 // on the left edge.
5864 if (viewLeft > 0) {
5865 mZoomCenterX = viewLeft * mDefaultScale
5866 / (mDefaultScale - mActualScale);
5867 } else {
5868 scrollBy(viewLeft, 0);
5869 mZoomCenterX = 0;
5870 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005871 }
Grace Klobac6f95fe2010-03-10 13:25:34 -08005872 zoomWithPreview(mDefaultScale, true);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005873 }
5874 }
5875
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005876 // Called by JNI to handle a touch on a node representing an email address,
5877 // address, or phone number
5878 private void overrideLoading(String url) {
5879 mCallbackProxy.uiOverrideUrlLoading(url);
5880 }
5881
5882 @Override
5883 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
5884 boolean result = false;
5885 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005886 result = mWebTextView.requestFocus(direction,
5887 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005888 } else {
5889 result = super.requestFocus(direction, previouslyFocusedRect);
5890 if (mWebViewCore.getSettings().getNeedInitialFocus()) {
5891 // For cases such as GMail, where we gain focus from a direction,
5892 // we want to move to the first available link.
5893 // FIXME: If there are no visible links, we may not want to
5894 int fakeKeyDirection = 0;
5895 switch(direction) {
5896 case View.FOCUS_UP:
5897 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
5898 break;
5899 case View.FOCUS_DOWN:
5900 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
5901 break;
5902 case View.FOCUS_LEFT:
5903 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
5904 break;
5905 case View.FOCUS_RIGHT:
5906 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
5907 break;
5908 default:
5909 return result;
5910 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005911 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005912 navHandledKey(fakeKeyDirection, 1, true, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005913 }
5914 }
5915 }
5916 return result;
5917 }
5918
5919 @Override
5920 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
5921 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
5922
5923 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
5924 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
5925 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
5926 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
5927
5928 int measuredHeight = heightSize;
5929 int measuredWidth = widthSize;
5930
5931 // Grab the content size from WebViewCore.
Grace Klobae621d6f2009-09-11 13:20:39 -07005932 int contentHeight = contentToViewDimension(mContentHeight);
5933 int contentWidth = contentToViewDimension(mContentWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005934
5935// Log.d(LOGTAG, "------- measure " + heightMode);
5936
5937 if (heightMode != MeasureSpec.EXACTLY) {
5938 mHeightCanMeasure = true;
5939 measuredHeight = contentHeight;
5940 if (heightMode == MeasureSpec.AT_MOST) {
5941 // If we are larger than the AT_MOST height, then our height can
5942 // no longer be measured and we should scroll internally.
5943 if (measuredHeight > heightSize) {
5944 measuredHeight = heightSize;
5945 mHeightCanMeasure = false;
5946 }
5947 }
5948 } else {
5949 mHeightCanMeasure = false;
5950 }
5951 if (mNativeClass != 0) {
5952 nativeSetHeightCanMeasure(mHeightCanMeasure);
5953 }
5954 // For the width, always use the given size unless unspecified.
5955 if (widthMode == MeasureSpec.UNSPECIFIED) {
5956 mWidthCanMeasure = true;
5957 measuredWidth = contentWidth;
5958 } else {
5959 mWidthCanMeasure = false;
5960 }
5961
5962 synchronized (this) {
5963 setMeasuredDimension(measuredWidth, measuredHeight);
5964 }
5965 }
5966
5967 @Override
5968 public boolean requestChildRectangleOnScreen(View child,
5969 Rect rect,
5970 boolean immediate) {
5971 rect.offset(child.getLeft() - child.getScrollX(),
5972 child.getTop() - child.getScrollY());
5973
Cary Clark31b83672010-03-09 09:20:34 -05005974 Rect content = new Rect(viewToContentX(mScrollX),
5975 viewToContentY(mScrollY),
5976 viewToContentX(mScrollX + getWidth()
5977 - getVerticalScrollbarWidth()),
5978 viewToContentY(mScrollY + getViewHeightWithTitle()));
5979 content = nativeSubtractLayers(content);
5980 int screenTop = contentToViewY(content.top);
5981 int screenBottom = contentToViewY(content.bottom);
5982 int height = screenBottom - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005983 int scrollYDelta = 0;
5984
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005985 if (rect.bottom > screenBottom) {
5986 int oneThirdOfScreenHeight = height / 3;
5987 if (rect.height() > 2 * oneThirdOfScreenHeight) {
5988 // If the rectangle is too tall to fit in the bottom two thirds
5989 // of the screen, place it at the top.
5990 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005991 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005992 // If the rectangle will still fit on screen, we want its
5993 // top to be in the top third of the screen.
5994 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005995 }
5996 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005997 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005998 }
5999
Cary Clark31b83672010-03-09 09:20:34 -05006000 int screenLeft = contentToViewX(content.left);
6001 int screenRight = contentToViewX(content.right);
6002 int width = screenRight - screenLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006003 int scrollXDelta = 0;
6004
6005 if (rect.right > screenRight && rect.left > screenLeft) {
6006 if (rect.width() > width) {
6007 scrollXDelta += (rect.left - screenLeft);
6008 } else {
6009 scrollXDelta += (rect.right - screenRight);
6010 }
6011 } else if (rect.left < screenLeft) {
6012 scrollXDelta -= (screenLeft - rect.left);
6013 }
6014
6015 if ((scrollYDelta | scrollXDelta) != 0) {
6016 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
6017 }
6018
6019 return false;
6020 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006021
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006022 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
6023 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04006024 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
6025 arg.mReplace = replace;
6026 arg.mNewStart = newStart;
6027 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07006028 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04006029 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006030 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
6031 }
6032
6033 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkded054c2009-06-15 10:26:08 -04006034 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
6035 arg.mEvent = event;
6036 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006037 // Increase our text generation number, and pass it to webcore thread
6038 mTextGeneration++;
6039 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
6040 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04006041 // To make sure the host application, like Browser, has the up to date
6042 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006043 // document state.
6044 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
6045 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04006046 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006047 }
6048
Andrei Popescu04098572010-03-09 12:23:19 +00006049 /* package */ synchronized WebViewCore getWebViewCore() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006050 return mWebViewCore;
6051 }
6052
6053 //-------------------------------------------------------------------------
6054 // Methods can be called from a separate thread, like WebViewCore
6055 // If it needs to call the View system, it has to send message.
6056 //-------------------------------------------------------------------------
6057
6058 /**
6059 * General handler to receive message coming from webkit thread
6060 */
6061 class PrivateHandler extends Handler {
6062 @Override
6063 public void handleMessage(Message msg) {
Cary Clark3e88ddc2009-10-08 14:59:46 -04006064 // exclude INVAL_RECT_MSG_ID since it is frequently output
6065 if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
Grace Klobac2242f22010-03-05 14:00:26 -08006066 if (msg.what >= FIRST_PRIVATE_MSG_ID
6067 && msg.what <= LAST_PRIVATE_MSG_ID) {
6068 Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
6069 - FIRST_PRIVATE_MSG_ID]);
6070 } else if (msg.what >= FIRST_PACKAGE_MSG_ID
6071 && msg.what <= LAST_PACKAGE_MSG_ID) {
6072 Log.v(LOGTAG, HandlerPackageDebugString[msg.what
6073 - FIRST_PACKAGE_MSG_ID]);
6074 } else {
6075 Log.v(LOGTAG, Integer.toString(msg.what));
6076 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006077 }
Grace Kloba207308a2009-09-27 11:44:14 -07006078 if (mWebViewCore == null) {
6079 // after WebView's destroy() is called, skip handling messages.
6080 return;
6081 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006082 switch (msg.what) {
6083 case REMEMBER_PASSWORD: {
6084 mDatabase.setUsernamePassword(
6085 msg.getData().getString("host"),
6086 msg.getData().getString("username"),
6087 msg.getData().getString("password"));
6088 ((Message) msg.obj).sendToTarget();
6089 break;
6090 }
6091 case NEVER_REMEMBER_PASSWORD: {
6092 mDatabase.setUsernamePassword(
6093 msg.getData().getString("host"), null, null);
6094 ((Message) msg.obj).sendToTarget();
6095 break;
6096 }
Grace Klobac2242f22010-03-05 14:00:26 -08006097 case PREVENT_DEFAULT_TIMEOUT: {
6098 // if timeout happens, cancel it so that it won't block UI
6099 // to continue handling touch events
6100 if ((msg.arg1 == MotionEvent.ACTION_DOWN
6101 && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES)
6102 || (msg.arg1 == MotionEvent.ACTION_MOVE
6103 && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN)) {
6104 cancelWebCoreTouchEvent(
6105 viewToContentX((int) mLastTouchX + mScrollX),
6106 viewToContentY((int) mLastTouchY + mScrollY),
6107 true);
Grace Klobaf58af622009-09-24 17:41:23 -07006108 }
Grace Klobac2242f22010-03-05 14:00:26 -08006109 break;
6110 }
6111 case SWITCH_TO_SHORTPRESS: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006112 if (mTouchMode == TOUCH_INIT_MODE) {
Grace Klobac2242f22010-03-05 14:00:26 -08006113 if (mPreventDefault != PREVENT_DEFAULT_YES) {
6114 mTouchMode = TOUCH_SHORTPRESS_START_MODE;
6115 updateSelection();
6116 } else {
6117 // set to TOUCH_SHORTPRESS_MODE so that it won't
6118 // trigger double tap any more
6119 mTouchMode = TOUCH_SHORTPRESS_MODE;
6120 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006121 } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
6122 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006123 }
6124 break;
6125 }
6126 case SWITCH_TO_LONGPRESS: {
Grace Klobac2242f22010-03-05 14:00:26 -08006127 if (inFullScreenMode() || mDeferTouchProcess) {
6128 TouchEventData ted = new TouchEventData();
Grace Kloba5f68d6f2009-12-08 18:42:54 -08006129 ted.mAction = WebViewCore.ACTION_LONGPRESS;
6130 ted.mX = viewToContentX((int) mLastTouchX + mScrollX);
6131 ted.mY = viewToContentY((int) mLastTouchY + mScrollY);
Grace Klobac2242f22010-03-05 14:00:26 -08006132 // metaState for long press is tricky. Should it be the
6133 // state when the press started or when the press was
6134 // released? Or some intermediary key state? For
6135 // simplicity for now, we don't set it.
Ben Murdoch8a032a32010-02-02 18:20:11 +00006136 ted.mMetaState = 0;
Grace Klobac2242f22010-03-05 14:00:26 -08006137 ted.mReprocess = mDeferTouchProcess;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08006138 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Grace Klobac2242f22010-03-05 14:00:26 -08006139 } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
Grace Kloba6c451b72009-06-25 12:25:30 -07006140 mTouchMode = TOUCH_DONE_MODE;
Grace Klobac2242f22010-03-05 14:00:26 -08006141 performLongClick();
6142 rebuildWebTextView();
Grace Kloba6c451b72009-06-25 12:25:30 -07006143 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006144 break;
6145 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006146 case RELEASE_SINGLE_TAP: {
Grace Klobac2242f22010-03-05 14:00:26 -08006147 doShortPress();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006148 break;
6149 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006150 case SCROLL_BY_MSG_ID:
6151 setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
6152 break;
6153 case SYNC_SCROLL_TO_MSG_ID:
6154 if (mUserScroll) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006155 // if user has scrolled explicitly, don't sync the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006156 // scroll position any more
6157 mUserScroll = false;
6158 break;
6159 }
6160 // fall through
6161 case SCROLL_TO_MSG_ID:
6162 if (setContentScrollTo(msg.arg1, msg.arg2)) {
6163 // if we can't scroll to the exact position due to pin,
Cary Clarkd6982c92009-05-29 11:02:22 -04006164 // send a message to WebCore to re-scroll when we get a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006165 // new picture
6166 mUserScroll = false;
6167 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
6168 msg.arg1, msg.arg2);
6169 }
6170 break;
6171 case SPAWN_SCROLL_TO_MSG_ID:
6172 spawnContentScrollTo(msg.arg1, msg.arg2);
6173 break;
Grace Kloba769ed212010-01-27 10:52:47 -08006174 case UPDATE_ZOOM_RANGE: {
6175 WebViewCore.RestoreState restoreState
6176 = (WebViewCore.RestoreState) msg.obj;
6177 // mScrollX contains the new minPrefWidth
6178 updateZoomRange(restoreState, getViewWidth(),
6179 restoreState.mScrollX, false);
6180 break;
6181 }
Grace Klobae397a882009-08-06 12:04:14 -07006182 case NEW_PICTURE_MSG_ID: {
6183 WebSettings settings = mWebViewCore.getSettings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006184 // called for new content
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006185 final int viewWidth = getViewWidth();
Cary Clarkd6982c92009-05-29 11:02:22 -04006186 final WebViewCore.DrawData draw =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006187 (WebViewCore.DrawData) msg.obj;
6188 final Point viewSize = draw.mViewPoint;
Grace Klobae397a882009-08-06 12:04:14 -07006189 boolean useWideViewport = settings.getUseWideViewPort();
Grace Klobaef347ef2009-07-30 11:20:32 -07006190 WebViewCore.RestoreState restoreState = draw.mRestoreState;
Grace Kloba9a67c822009-12-20 11:33:58 -08006191 boolean hasRestoreState = restoreState != null;
6192 if (hasRestoreState) {
Grace Kloba769ed212010-01-27 10:52:47 -08006193 updateZoomRange(restoreState, viewSize.x,
6194 draw.mMinPrefWidth, true);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006195 if (!mDrawHistory) {
6196 mInZoomOverview = false;
6197
6198 if (mInitialScaleInPercent > 0) {
6199 setNewZoomScale(mInitialScaleInPercent / 100.0f,
Grace Kloba3a0def22010-01-23 21:11:54 -08006200 mInitialScaleInPercent != mTextWrapScale * 100,
6201 false);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006202 } else if (restoreState.mViewScale > 0) {
6203 mTextWrapScale = restoreState.mTextWrapScale;
6204 setNewZoomScale(restoreState.mViewScale, false,
Grace Kloba3a0def22010-01-23 21:11:54 -08006205 false);
Grace Kloba3a0def22010-01-23 21:11:54 -08006206 } else {
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006207 mInZoomOverview = useWideViewport
6208 && settings.getLoadWithOverviewMode();
6209 float scale;
6210 if (mInZoomOverview) {
6211 scale = (float) viewWidth
6212 / DEFAULT_VIEWPORT_WIDTH;
6213 } else {
6214 scale = restoreState.mTextWrapScale;
6215 }
6216 setNewZoomScale(scale, Math.abs(scale
Grace Klobac6f95fe2010-03-10 13:25:34 -08006217 - mTextWrapScale) >= MINIMUM_SCALE_INCREMENT,
6218 false);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006219 }
6220 setContentScrollTo(restoreState.mScrollX,
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04006221 restoreState.mScrollY);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006222 // As we are on a new page, remove the WebTextView. This
6223 // is necessary for page loads driven by webkit, and in
6224 // particular when the user was on a password field, so
6225 // the WebTextView was visible.
6226 clearTextEntry(false);
6227 // update the zoom buttons as the scale can be changed
6228 if (getSettings().getBuiltInZoomControls()) {
6229 updateZoomButtonsEnabled();
6230 }
Mike Reed8b302092009-11-12 12:50:20 -05006231 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006232 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006233 // We update the layout (i.e. request a layout from the
6234 // view system) if the last view size that we sent to
6235 // WebCore matches the view size of the picture we just
6236 // received in the fixed dimension.
6237 final boolean updateLayout = viewSize.x == mLastWidthSent
6238 && viewSize.y == mLastHeightSent;
Cary Clarkd6982c92009-05-29 11:02:22 -04006239 recordNewContentSize(draw.mWidthHeight.x,
Cary Clark5bb6b522009-09-21 11:58:31 -04006240 draw.mWidthHeight.y
6241 + (mFindIsUp ? mFindHeight : 0), updateLayout);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006242 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006243 Rect b = draw.mInvalRegion.getBounds();
6244 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
6245 b.left+","+b.top+","+b.right+","+b.bottom+"}");
6246 }
Mike Reede9e86b82009-09-15 11:26:53 -04006247 invalidateContentRect(draw.mInvalRegion.getBounds());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006248 if (mPictureListener != null) {
6249 mPictureListener.onNewPicture(WebView.this, capturePicture());
6250 }
Grace Klobaef347ef2009-07-30 11:20:32 -07006251 if (useWideViewport) {
Grace Kloba3a0def22010-01-23 21:11:54 -08006252 // limit mZoomOverviewWidth upper bound to
6253 // sMaxViewportWidth so that if the page doesn't behave
6254 // well, the WebView won't go insane. limit the lower
6255 // bound to match the default scale for mobile sites.
Grace Klobaa4fa1072009-11-09 12:01:50 -08006256 mZoomOverviewWidth = Math.min(sMaxViewportWidth, Math
Grace Kloba3a0def22010-01-23 21:11:54 -08006257 .max((int) (viewWidth / mDefaultScale), Math
6258 .max(draw.mMinPrefWidth,
6259 draw.mViewPoint.x)));
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006260 }
6261 if (!mMinZoomScaleFixed) {
Grace Klobae397a882009-08-06 12:04:14 -07006262 mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006263 }
6264 if (!mDrawHistory && mInZoomOverview) {
6265 // fit the content width to the current view. Ignore
6266 // the rounding error case.
6267 if (Math.abs((viewWidth * mInvActualScale)
6268 - mZoomOverviewWidth) > 1) {
Grace Klobae397a882009-08-06 12:04:14 -07006269 setNewZoomScale((float) viewWidth
Grace Kloba3a0def22010-01-23 21:11:54 -08006270 / mZoomOverviewWidth, Math.abs(mActualScale
Grace Klobac6f95fe2010-03-10 13:25:34 -08006271 - mTextWrapScale) < MINIMUM_SCALE_INCREMENT,
6272 false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006273 }
6274 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04006275 if (draw.mFocusSizeChanged && inEditingMode()) {
6276 mFocusSizeChanged = true;
6277 }
Grace Kloba9a67c822009-12-20 11:33:58 -08006278 if (hasRestoreState) {
6279 mViewManager.postReadyToDrawAll();
6280 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006281 break;
Grace Klobae397a882009-08-06 12:04:14 -07006282 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006283 case WEBCORE_INITIALIZED_MSG_ID:
6284 // nativeCreate sets mNativeClass to a non-zero value
6285 nativeCreate(msg.arg1);
6286 break;
6287 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
6288 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04006289 // and representing the same node as the pointer.
6290 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006291 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006292 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006293 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006294 int start = Selection.getSelectionStart(text);
6295 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006296 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006297 // Restore the selection, which may have been
6298 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006299 Spannable pword =
6300 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006301 Selection.setSelection(pword, start, end);
6302 // If the text entry has created more events, ignore
6303 // this one.
6304 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006305 mWebTextView.setTextAndKeepSelection(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006306 (String) msg.obj);
6307 }
6308 }
6309 break;
Leon Scrogginsb4157792010-03-18 12:42:33 -04006310 case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
6311 displaySoftKeyboard(true);
6312 updateTextSelectionFromMessage(msg.arg1, msg.arg2,
6313 (WebViewCore.TextSelectionData) msg.obj);
6314 break;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006315 case UPDATE_TEXT_SELECTION_MSG_ID:
Leon Scrogginsbd2754402010-02-09 17:51:08 -05006316 // If no textfield was in focus, and the user touched one,
6317 // causing it to send this message, then WebTextView has not
6318 // been set up yet. Rebuild it so it can set its selection.
6319 rebuildWebTextView();
Leon Scrogginsb4157792010-03-18 12:42:33 -04006320 updateTextSelectionFromMessage(msg.arg1, msg.arg2,
6321 (WebViewCore.TextSelectionData) msg.obj);
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006322 break;
Leon Scroggins3a503392010-01-06 17:04:38 -05006323 case RETURN_LABEL:
6324 if (inEditingMode()
6325 && mWebTextView.isSameTextField(msg.arg1)) {
6326 mWebTextView.setHint((String) msg.obj);
6327 InputMethodManager imm
6328 = InputMethodManager.peekInstance();
6329 // The hint is propagated to the IME in
6330 // onCreateInputConnection. If the IME is already
6331 // active, restart it so that its hint text is updated.
6332 if (imm != null && imm.isActive(mWebTextView)) {
6333 imm.restartInput(mWebTextView);
6334 }
6335 }
6336 break;
Cary Clark215b72c2009-06-26 14:38:43 -04006337 case MOVE_OUT_OF_PLUGIN:
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05006338 navHandledKey(msg.arg1, 1, false, 0);
Cary Clark215b72c2009-06-26 14:38:43 -04006339 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006340 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04006341 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006342 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04006343 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006344 mWebTextView.bringIntoView();
Leon Scroggins4890feb2009-07-02 10:37:10 -04006345 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006346 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006347 break;
Cary Clark243ea062009-06-25 10:49:32 -04006348 case CLEAR_TEXT_ENTRY:
Leon Scroggins6088e832010-02-17 13:17:32 -05006349 clearTextEntry(false);
Cary Clark243ea062009-06-25 10:49:32 -04006350 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006351 case INVAL_RECT_MSG_ID: {
6352 Rect r = (Rect)msg.obj;
6353 if (r == null) {
6354 invalidate();
6355 } else {
6356 // we need to scale r from content into view coords,
6357 // which viewInvalidate() does for us
6358 viewInvalidate(r.left, r.top, r.right, r.bottom);
6359 }
6360 break;
6361 }
Nicolas Roard38863332010-01-04 19:30:55 +00006362 case IMMEDIATE_REPAINT_MSG_ID: {
Nicolas Roard38863332010-01-04 19:30:55 +00006363 invalidate();
6364 break;
6365 }
6366 case SET_ROOT_LAYER_MSG_ID: {
Cary Clark2ec30692010-02-23 10:50:38 -05006367 nativeSetRootLayer(msg.arg1);
Nicolas Roard38863332010-01-04 19:30:55 +00006368 invalidate();
6369 break;
6370 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006371 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04006372 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006373 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006374 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006375 }
6376 break;
Grace Kloba96949ef2010-01-25 09:53:01 -08006377 case RESUME_WEBCORE_PRIORITY:
Grace Klobaa7bc87c2010-01-29 14:56:25 -08006378 WebViewCore.resumePriority();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006379 break;
6380
Leon Scrogginse3225672009-06-03 15:53:13 -04006381 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006382 // as this is shared by keydown and trackballdown, reset all
6383 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04006384 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006385 mTrackballDown = false;
Grace Kloba98e6fcf2010-01-27 15:20:30 -08006386 performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006387 break;
6388
6389 case WEBCORE_NEED_TOUCH_EVENTS:
6390 mForwardTouchEvents = (msg.arg1 != 0);
6391 break;
6392
6393 case PREVENT_TOUCH_ID:
Grace Klobac2242f22010-03-05 14:00:26 -08006394 if (inFullScreenMode()) {
6395 break;
6396 }
6397 if (msg.obj == null) {
6398 if (msg.arg1 == MotionEvent.ACTION_DOWN
6399 && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {
6400 // if prevent default is called from WebCore, UI
6401 // will not handle the rest of the touch events any
6402 // more.
6403 mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES
6404 : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN;
6405 } else if (msg.arg1 == MotionEvent.ACTION_MOVE
6406 && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
6407 // the return for the first ACTION_MOVE will decide
6408 // whether UI will handle touch or not. Currently no
6409 // support for alternating prevent default
6410 mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES
6411 : PREVENT_DEFAULT_NO;
6412 }
6413 } else if (msg.arg2 == 0) {
6414 // prevent default is not called in WebCore, so the
6415 // message needs to be reprocessed in UI
6416 TouchEventData ted = (TouchEventData) msg.obj;
6417 switch (ted.mAction) {
6418 case MotionEvent.ACTION_DOWN:
6419 mLastDeferTouchX = contentToViewX(ted.mX)
6420 - mScrollX;
6421 mLastDeferTouchY = contentToViewY(ted.mY)
6422 - mScrollY;
6423 mDeferTouchMode = TOUCH_INIT_MODE;
6424 break;
6425 case MotionEvent.ACTION_MOVE: {
6426 // no snapping in defer process
6427 int x = contentToViewX(ted.mX) - mScrollX;
6428 int y = contentToViewY(ted.mY) - mScrollY;
6429 if (mDeferTouchMode != TOUCH_DRAG_MODE) {
6430 mDeferTouchMode = TOUCH_DRAG_MODE;
6431 mLastDeferTouchX = x;
6432 mLastDeferTouchY = y;
6433 startDrag();
6434 }
6435 doDrag((int) (mLastDeferTouchX - x),
6436 (int) (mLastDeferTouchY - y));
6437 mLastDeferTouchX = x;
6438 mLastDeferTouchY = y;
6439 break;
Grace Klobaf58af622009-09-24 17:41:23 -07006440 }
Grace Klobac2242f22010-03-05 14:00:26 -08006441 case MotionEvent.ACTION_UP:
6442 case MotionEvent.ACTION_CANCEL:
6443 if (mDeferTouchMode == TOUCH_DRAG_MODE) {
6444 // no fling in defer process
6445 mScroller.springback(mScrollX, mScrollY, 0,
6446 computeMaxScrollX(), 0,
6447 computeMaxScrollY());
6448 invalidate();
6449 WebViewCore.resumePriority();
6450 }
6451 mDeferTouchMode = TOUCH_DONE_MODE;
6452 break;
6453 case WebViewCore.ACTION_DOUBLETAP:
6454 // doDoubleTap() needs mLastTouchX/Y as anchor
6455 mLastTouchX = contentToViewX(ted.mX) - mScrollX;
6456 mLastTouchY = contentToViewY(ted.mY) - mScrollY;
6457 doDoubleTap();
6458 mDeferTouchMode = TOUCH_DONE_MODE;
6459 break;
6460 case WebViewCore.ACTION_LONGPRESS:
6461 HitTestResult hitTest = getHitTestResult();
6462 if (hitTest != null && hitTest.mType
6463 != HitTestResult.UNKNOWN_TYPE) {
6464 performLongClick();
6465 rebuildWebTextView();
6466 }
6467 mDeferTouchMode = TOUCH_DONE_MODE;
6468 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006469 }
6470 }
6471 break;
6472
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04006473 case REQUEST_KEYBOARD:
6474 if (msg.arg1 == 0) {
6475 hideSoftKeyboard();
6476 } else {
Leon Scrogginsb4157792010-03-18 12:42:33 -04006477 displaySoftKeyboard(false);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04006478 }
6479 break;
6480
Leon Scroggins5de63892009-10-29 09:48:43 -04006481 case FIND_AGAIN:
6482 // Ignore if find has been dismissed.
6483 if (mFindIsUp) {
6484 findAll(mLastFind);
6485 }
6486 break;
6487
Cary Clark25415e22009-10-12 13:41:28 -04006488 case DRAG_HELD_MOTIONLESS:
6489 mHeldMotionless = MOTIONLESS_TRUE;
6490 invalidate();
6491 // fall through to keep scrollbars awake
6492
6493 case AWAKEN_SCROLL_BARS:
6494 if (mTouchMode == TOUCH_DRAG_MODE
6495 && mHeldMotionless == MOTIONLESS_TRUE) {
6496 awakenScrollBars(ViewConfiguration
6497 .getScrollDefaultDelay(), false);
6498 mPrivateHandler.sendMessageDelayed(mPrivateHandler
6499 .obtainMessage(AWAKEN_SCROLL_BARS),
6500 ViewConfiguration.getScrollDefaultDelay());
6501 }
6502 break;
Cary Clark1cb97ee2009-12-11 12:10:36 -05006503
6504 case DO_MOTION_UP:
Cary Clarkbad0c542010-01-11 14:58:21 -05006505 doMotionUp(msg.arg1, msg.arg2);
Cary Clark1cb97ee2009-12-11 12:10:36 -05006506 break;
6507
Grace Kloba3a0def22010-01-23 21:11:54 -08006508 case SHOW_FULLSCREEN: {
Derek Sollenbergerc28ff442010-03-09 17:38:49 -05006509 View view = (View) msg.obj;
6510 int npp = msg.arg1;
Grace Kloba11438c32009-12-16 11:39:12 -08006511
Derek Sollenbergerc28ff442010-03-09 17:38:49 -05006512 if (mFullScreenHolder != null) {
6513 Log.w(LOGTAG, "Should not have another full screen.");
6514 mFullScreenHolder.dismiss();
6515 }
6516 mFullScreenHolder = new PluginFullScreenHolder(WebView.this, npp);
6517 mFullScreenHolder.setContentView(view);
6518 mFullScreenHolder.setCancelable(false);
6519 mFullScreenHolder.setCanceledOnTouchOutside(false);
6520 mFullScreenHolder.show();
6521
6522 break;
6523 }
Grace Kloba11438c32009-12-16 11:39:12 -08006524 case HIDE_FULLSCREEN:
Grace Klobac2242f22010-03-05 14:00:26 -08006525 if (inFullScreenMode()) {
Grace Kloba11438c32009-12-16 11:39:12 -08006526 mFullScreenHolder.dismiss();
6527 mFullScreenHolder = null;
6528 }
6529 break;
6530
Leon Scrogginse26efa32009-12-15 16:38:45 -05006531 case DOM_FOCUS_CHANGED:
6532 if (inEditingMode()) {
6533 nativeClearCursor();
6534 rebuildWebTextView();
6535 }
6536 break;
6537
Grace Kloba3a0def22010-01-23 21:11:54 -08006538 case SHOW_RECT_MSG_ID: {
6539 WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
6540 int x = mScrollX;
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08006541 int left = contentToViewX(data.mLeft);
Grace Kloba3a0def22010-01-23 21:11:54 -08006542 int width = contentToViewDimension(data.mWidth);
6543 int maxWidth = contentToViewDimension(data.mContentWidth);
6544 int viewWidth = getViewWidth();
6545 if (width < viewWidth) {
6546 // center align
6547 x += left + width / 2 - mScrollX - viewWidth / 2;
6548 } else {
6549 x += (int) (left + data.mXPercentInDoc * width
6550 - mScrollX - data.mXPercentInView * viewWidth);
6551 }
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08006552 if (DebugFlags.WEB_VIEW) {
6553 Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
6554 width + ",maxWidth=" + maxWidth +
6555 ",viewWidth=" + viewWidth + ",x="
6556 + x + ",xPercentInDoc=" + data.mXPercentInDoc +
6557 ",xPercentInView=" + data.mXPercentInView+ ")");
6558 }
Grace Kloba3a0def22010-01-23 21:11:54 -08006559 // use the passing content width to cap x as the current
6560 // mContentWidth may not be updated yet
6561 x = Math.max(0,
6562 (Math.min(maxWidth, x + viewWidth)) - viewWidth);
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08006563 int top = contentToViewY(data.mTop);
Grace Kloba3a0def22010-01-23 21:11:54 -08006564 int height = contentToViewDimension(data.mHeight);
6565 int maxHeight = contentToViewDimension(data.mContentHeight);
6566 int viewHeight = getViewHeight();
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08006567 int y = (int) (top + data.mYPercentInDoc * height -
6568 data.mYPercentInView * viewHeight);
6569 if (DebugFlags.WEB_VIEW) {
6570 Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
6571 height + ",maxHeight=" + maxHeight +
6572 ",viewHeight=" + viewHeight + ",y="
6573 + y + ",yPercentInDoc=" + data.mYPercentInDoc +
6574 ",yPercentInView=" + data.mYPercentInView+ ")");
Grace Kloba3a0def22010-01-23 21:11:54 -08006575 }
6576 // use the passing content height to cap y as the current
6577 // mContentHeight may not be updated yet
6578 y = Math.max(0,
6579 (Math.min(maxHeight, y + viewHeight) - viewHeight));
Shimeng (Simon) Wangb7f17d42010-02-08 15:17:21 -08006580 // We need to take into account the visible title height
6581 // when scrolling since y is an absolute view position.
6582 y = Math.max(0, y - getVisibleTitleHeight());
Grace Kloba3a0def22010-01-23 21:11:54 -08006583 scrollTo(x, y);
6584 }
6585 break;
6586
Grace Klobae8300a12010-03-12 13:32:55 -08006587 case CENTER_FIT_RECT:
6588 Rect r = (Rect)msg.obj;
6589 mInZoomOverview = false;
6590 centerFitRect(r.left, r.top, r.width(), r.height());
6591 break;
6592
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006593 default:
6594 super.handleMessage(msg);
6595 break;
6596 }
6597 }
6598 }
6599
Leon Scrogginsb4157792010-03-18 12:42:33 -04006600 /**
6601 * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
6602 * and UPDATE_TEXT_SELECTION_MSG_ID. Update the selection of WebTextView.
6603 */
6604 private void updateTextSelectionFromMessage(int nodePointer,
6605 int textGeneration, WebViewCore.TextSelectionData data) {
6606 if (inEditingMode()
6607 && mWebTextView.isSameTextField(nodePointer)
6608 && textGeneration == mTextGeneration) {
6609 mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
6610 }
6611 }
6612
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006613 // Class used to use a dropdown for a <select> element
6614 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006615 // Whether the listbox allows multiple selection.
6616 private boolean mMultiple;
6617 // Passed in to a list with multiple selection to tell
6618 // which items are selected.
6619 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04006620 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006621 // where the initial selection is.
6622 private int mSelection;
6623
6624 private Container[] mContainers;
6625
6626 // Need these to provide stable ids to my ArrayAdapter,
6627 // which normally does not have stable ids. (Bug 1250098)
6628 private class Container extends Object {
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006629 /**
6630 * Possible values for mEnabled. Keep in sync with OptionStatus in
6631 * WebViewCore.cpp
6632 */
6633 final static int OPTGROUP = -1;
6634 final static int OPTION_DISABLED = 0;
6635 final static int OPTION_ENABLED = 1;
6636
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006637 String mString;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006638 int mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006639 int mId;
6640
6641 public String toString() {
6642 return mString;
6643 }
6644 }
6645
6646 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04006647 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006648 * and allow filtering.
6649 */
6650 private class MyArrayListAdapter extends ArrayAdapter<Container> {
6651 public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006652 super(context,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006653 multiple ? com.android.internal.R.layout.select_dialog_multichoice :
Cary Clarkd6982c92009-05-29 11:02:22 -04006654 com.android.internal.R.layout.select_dialog_singlechoice,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006655 objects);
6656 }
6657
6658 @Override
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006659 public View getView(int position, View convertView,
6660 ViewGroup parent) {
6661 // Always pass in null so that we will get a new CheckedTextView
6662 // Otherwise, an item which was previously used as an <optgroup>
6663 // element (i.e. has no check), could get used as an <option>
6664 // element, which needs a checkbox/radio, but it would not have
6665 // one.
6666 convertView = super.getView(position, null, parent);
6667 Container c = item(position);
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -04006668 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
6669 // ListView does not draw dividers between disabled and
6670 // enabled elements. Use a LinearLayout to provide dividers
6671 LinearLayout layout = new LinearLayout(mContext);
6672 layout.setOrientation(LinearLayout.VERTICAL);
6673 if (position > 0) {
6674 View dividerTop = new View(mContext);
6675 dividerTop.setBackgroundResource(
6676 android.R.drawable.divider_horizontal_bright);
6677 layout.addView(dividerTop);
6678 }
6679
6680 if (Container.OPTGROUP == c.mEnabled) {
6681 // Currently select_dialog_multichoice and
6682 // select_dialog_singlechoice are CheckedTextViews. If
6683 // that changes, the class cast will no longer be valid.
6684 Assert.assertTrue(
6685 convertView instanceof CheckedTextView);
6686 ((CheckedTextView) convertView).setCheckMarkDrawable(
6687 null);
6688 } else {
6689 // c.mEnabled == Container.OPTION_DISABLED
6690 // Draw the disabled element in a disabled state.
6691 convertView.setEnabled(false);
6692 }
6693
6694 layout.addView(convertView);
6695 if (position < getCount() - 1) {
6696 View dividerBottom = new View(mContext);
6697 dividerBottom.setBackgroundResource(
6698 android.R.drawable.divider_horizontal_bright);
6699 layout.addView(dividerBottom);
6700 }
6701 return layout;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006702 }
6703 return convertView;
6704 }
6705
6706 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006707 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04006708 // AdapterView's onChanged method uses this to determine whether
6709 // to restore the old state. Return false so that the old (out
6710 // of date) state does not replace the new, valid state.
6711 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006712 }
6713
6714 private Container item(int position) {
6715 if (position < 0 || position >= getCount()) {
6716 return null;
6717 }
6718 return (Container) getItem(position);
6719 }
6720
6721 @Override
6722 public long getItemId(int position) {
6723 Container item = item(position);
6724 if (item == null) {
6725 return -1;
6726 }
6727 return item.mId;
6728 }
6729
6730 @Override
6731 public boolean areAllItemsEnabled() {
6732 return false;
6733 }
6734
6735 @Override
6736 public boolean isEnabled(int position) {
6737 Container item = item(position);
6738 if (item == null) {
6739 return false;
6740 }
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006741 return Container.OPTION_ENABLED == item.mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006742 }
6743 }
6744
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006745 private InvokeListBox(String[] array, int[] enabled, int[] selected) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006746 mMultiple = true;
6747 mSelectedArray = selected;
6748
6749 int length = array.length;
6750 mContainers = new Container[length];
6751 for (int i = 0; i < length; i++) {
6752 mContainers[i] = new Container();
6753 mContainers[i].mString = array[i];
6754 mContainers[i].mEnabled = enabled[i];
6755 mContainers[i].mId = i;
6756 }
6757 }
6758
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006759 private InvokeListBox(String[] array, int[] enabled, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006760 mSelection = selection;
6761 mMultiple = false;
6762
6763 int length = array.length;
6764 mContainers = new Container[length];
6765 for (int i = 0; i < length; i++) {
6766 mContainers[i] = new Container();
6767 mContainers[i].mString = array[i];
6768 mContainers[i].mEnabled = enabled[i];
6769 mContainers[i].mId = i;
6770 }
6771 }
6772
Leon Scroggins3667ce42009-05-13 15:58:03 -04006773 /*
6774 * Whenever the data set changes due to filtering, this class ensures
6775 * that the checked item remains checked.
6776 */
6777 private class SingleDataSetObserver extends DataSetObserver {
6778 private long mCheckedId;
6779 private ListView mListView;
6780 private Adapter mAdapter;
6781
6782 /*
6783 * Create a new observer.
6784 * @param id The ID of the item to keep checked.
6785 * @param l ListView for getting and clearing the checked states
6786 * @param a Adapter for getting the IDs
6787 */
6788 public SingleDataSetObserver(long id, ListView l, Adapter a) {
6789 mCheckedId = id;
6790 mListView = l;
6791 mAdapter = a;
6792 }
6793
6794 public void onChanged() {
6795 // The filter may have changed which item is checked. Find the
6796 // item that the ListView thinks is checked.
6797 int position = mListView.getCheckedItemPosition();
6798 long id = mAdapter.getItemId(position);
6799 if (mCheckedId != id) {
6800 // Clear the ListView's idea of the checked item, since
6801 // it is incorrect
6802 mListView.clearChoices();
6803 // Search for mCheckedId. If it is in the filtered list,
6804 // mark it as checked
6805 int count = mAdapter.getCount();
6806 for (int i = 0; i < count; i++) {
6807 if (mAdapter.getItemId(i) == mCheckedId) {
6808 mListView.setItemChecked(i, true);
6809 break;
6810 }
6811 }
6812 }
6813 }
6814
6815 public void onInvalidate() {}
6816 }
6817
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006818 public void run() {
6819 final ListView listView = (ListView) LayoutInflater.from(mContext)
6820 .inflate(com.android.internal.R.layout.select_dialog, null);
Cary Clarkd6982c92009-05-29 11:02:22 -04006821 final MyArrayListAdapter adapter = new
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006822 MyArrayListAdapter(mContext, mContainers, mMultiple);
6823 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
6824 .setView(listView).setCancelable(true)
6825 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04006826
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006827 if (mMultiple) {
6828 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
6829 public void onClick(DialogInterface dialog, int which) {
6830 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04006831 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006832 adapter.getCount(), 0,
6833 listView.getCheckedItemPositions());
6834 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07006835 b.setNegativeButton(android.R.string.cancel,
6836 new DialogInterface.OnClickListener() {
6837 public void onClick(DialogInterface dialog, int which) {
6838 mWebViewCore.sendMessage(
6839 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
6840 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006841 }
6842 final AlertDialog dialog = b.create();
6843 listView.setAdapter(adapter);
6844 listView.setFocusableInTouchMode(true);
6845 // There is a bug (1250103) where the checks in a ListView with
6846 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04006847 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006848 // filtered. Do not allow filtering on multiple lists until
6849 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04006850
Leon Scroggins3667ce42009-05-13 15:58:03 -04006851 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006852 if (mMultiple) {
6853 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
6854 int length = mSelectedArray.length;
6855 for (int i = 0; i < length; i++) {
6856 listView.setItemChecked(mSelectedArray[i], true);
6857 }
6858 } else {
6859 listView.setOnItemClickListener(new OnItemClickListener() {
6860 public void onItemClick(AdapterView parent, View v,
6861 int position, long id) {
6862 mWebViewCore.sendMessage(
6863 EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
6864 dialog.dismiss();
6865 }
6866 });
6867 if (mSelection != -1) {
6868 listView.setSelection(mSelection);
6869 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
6870 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04006871 DataSetObserver observer = new SingleDataSetObserver(
6872 adapter.getItemId(mSelection), listView, adapter);
6873 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006874 }
6875 }
6876 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
6877 public void onCancel(DialogInterface dialog) {
6878 mWebViewCore.sendMessage(
6879 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
6880 }
6881 });
6882 dialog.show();
6883 }
6884 }
6885
6886 /*
6887 * Request a dropdown menu for a listbox with multiple selection.
6888 *
6889 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006890 * @param enabledArray State for each element in the list. See static
6891 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006892 * @param selectedArray Which positions are initally selected.
6893 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006894 void requestListBox(String[] array, int[] enabledArray, int[]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006895 selectedArray) {
6896 mPrivateHandler.post(
6897 new InvokeListBox(array, enabledArray, selectedArray));
6898 }
6899
Grace Kloba769ed212010-01-27 10:52:47 -08006900 private void updateZoomRange(WebViewCore.RestoreState restoreState,
6901 int viewWidth, int minPrefWidth, boolean updateZoomOverview) {
6902 if (restoreState.mMinScale == 0) {
6903 if (restoreState.mMobileSite) {
6904 if (minPrefWidth > Math.max(0, viewWidth)) {
6905 mMinZoomScale = (float) viewWidth / minPrefWidth;
6906 mMinZoomScaleFixed = false;
6907 if (updateZoomOverview) {
6908 WebSettings settings = getSettings();
6909 mInZoomOverview = settings.getUseWideViewPort() &&
6910 settings.getLoadWithOverviewMode();
6911 }
6912 } else {
6913 mMinZoomScale = restoreState.mDefaultScale;
6914 mMinZoomScaleFixed = true;
6915 }
6916 } else {
6917 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
6918 mMinZoomScaleFixed = false;
6919 }
6920 } else {
6921 mMinZoomScale = restoreState.mMinScale;
6922 mMinZoomScaleFixed = true;
6923 }
6924 if (restoreState.mMaxScale == 0) {
6925 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
6926 } else {
6927 mMaxZoomScale = restoreState.mMaxScale;
6928 }
6929 }
6930
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006931 /*
6932 * Request a dropdown menu for a listbox with single selection or a single
6933 * <select> element.
6934 *
6935 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006936 * @param enabledArray State for each element in the list. See static
6937 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006938 * @param selection Which position is initally selected.
6939 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006940 void requestListBox(String[] array, int[] enabledArray, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006941 mPrivateHandler.post(
6942 new InvokeListBox(array, enabledArray, selection));
6943 }
6944
6945 // called by JNI
Leon Scroggins47fabbf2009-12-08 16:57:26 -05006946 private void sendMoveFocus(int frame, int node) {
6947 mWebViewCore.sendMessage(EventHub.SET_MOVE_FOCUS,
6948 new WebViewCore.CursorData(frame, node, 0, 0));
6949 }
6950
6951 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04006952 private void sendMoveMouse(int frame, int node, int x, int y) {
6953 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
6954 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006955 }
6956
Leon Scroggins0658e8f2009-06-26 14:09:09 -04006957 /*
6958 * Send a mouse move event to the webcore thread.
6959 *
6960 * @param removeFocus Pass true if the "mouse" cursor is now over a node
6961 * which wants key events, but it is not the focus. This
6962 * will make the visual appear as though nothing is in
6963 * focus. Remove the WebTextView, if present, and stop
6964 * drawing the blinking caret.
6965 * called by JNI
6966 */
6967 private void sendMoveMouseIfLatest(boolean removeFocus) {
6968 if (removeFocus) {
Leon Scroggins6088e832010-02-17 13:17:32 -05006969 clearTextEntry(true);
Cary Clark19436562009-06-04 16:25:07 -04006970 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04006971 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
6972 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006973 }
6974
6975 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04006976 private void sendMotionUp(int touchGeneration,
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006977 int frame, int node, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006978 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
6979 touchUpData.mMoveGeneration = touchGeneration;
Cary Clarkd6982c92009-05-29 11:02:22 -04006980 touchUpData.mFrame = frame;
6981 touchUpData.mNode = node;
6982 touchUpData.mX = x;
6983 touchUpData.mY = y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006984 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
6985 }
6986
6987
6988 private int getScaledMaxXScroll() {
6989 int width;
6990 if (mHeightCanMeasure == false) {
6991 width = getViewWidth() / 4;
6992 } else {
6993 Rect visRect = new Rect();
6994 calcOurVisibleRect(visRect);
6995 width = visRect.width() / 2;
6996 }
6997 // FIXME the divisor should be retrieved from somewhere
Leon Scroggins0236e672009-09-02 21:12:08 -04006998 return viewToContentX(width);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006999 }
7000
7001 private int getScaledMaxYScroll() {
7002 int height;
7003 if (mHeightCanMeasure == false) {
7004 height = getViewHeight() / 4;
7005 } else {
7006 Rect visRect = new Rect();
7007 calcOurVisibleRect(visRect);
7008 height = visRect.height() / 2;
7009 }
7010 // FIXME the divisor should be retrieved from somewhere
7011 // the closest thing today is hard-coded into ScrollView.java
7012 // (from ScrollView.java, line 363) int maxJump = height/2;
Cary Clarkdf344372009-09-15 12:47:39 -04007013 return Math.round(height * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007014 }
7015
7016 /**
7017 * Called by JNI to invalidate view
7018 */
7019 private void viewInvalidate() {
7020 invalidate();
7021 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007022
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05007023 /**
7024 * Pass the key to the plugin. This assumes that nativeFocusIsPlugin()
7025 * returned true.
7026 */
7027 private void letPluginHandleNavKey(int keyCode, long time, boolean down) {
7028 int keyEventAction;
7029 int eventHubAction;
7030 if (down) {
7031 keyEventAction = KeyEvent.ACTION_DOWN;
7032 eventHubAction = EventHub.KEY_DOWN;
7033 playSoundEffect(keyCodeToSoundsEffect(keyCode));
7034 } else {
7035 keyEventAction = KeyEvent.ACTION_UP;
7036 eventHubAction = EventHub.KEY_UP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007037 }
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05007038 KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
7039 1, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
Cary Clark215b72c2009-06-26 14:38:43 -04007040 | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
7041 | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
7042 , 0, 0, 0);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05007043 mWebViewCore.sendMessage(eventHubAction, event);
7044 }
7045
7046 // return true if the key was handled
7047 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
7048 long time) {
7049 if (mNativeClass == 0) {
7050 return false;
Cary Clark215b72c2009-06-26 14:38:43 -04007051 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007052 mLastCursorTime = time;
7053 mLastCursorBounds = nativeGetCursorRingBounds();
7054 boolean keyHandled
7055 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04007056 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007057 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
7058 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007059 + " handled=" + keyHandled);
7060 }
7061 if (keyHandled == false || mHeightCanMeasure == false) {
7062 return keyHandled;
7063 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007064 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
7065 if (contentCursorRingBounds.isEmpty()) return keyHandled;
Mike Reede9e86b82009-09-15 11:26:53 -04007066 Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007067 Rect visRect = new Rect();
7068 calcOurVisibleRect(visRect);
7069 Rect outset = new Rect(visRect);
7070 int maxXScroll = visRect.width() / 2;
7071 int maxYScroll = visRect.height() / 2;
7072 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007073 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007074 return keyHandled;
7075 }
7076 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007077 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
7078 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007079 if (maxH > 0) {
7080 pinScrollBy(maxH, 0, true, 0);
7081 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007082 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
7083 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007084 if (maxH < 0) {
7085 pinScrollBy(maxH, 0, true, 0);
7086 }
7087 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007088 if (mLastCursorBounds.isEmpty()) return keyHandled;
7089 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
7090 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007091 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007092 if (DebugFlags.WEB_VIEW) {
7093 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
7094 + contentCursorRingBounds);
7095 }
7096 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007097 mUserScroll = true;
7098 return keyHandled;
7099 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007101 /**
7102 * Set the background color. It's white by default. Pass
7103 * zero to make the view transparent.
7104 * @param color the ARGB color described by Color.java
7105 */
7106 public void setBackgroundColor(int color) {
7107 mBackgroundColor = color;
7108 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
7109 }
7110
7111 public void debugDump() {
7112 nativeDebugDump();
7113 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
7114 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007115
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007116 /**
Mike Reedefe2c722009-10-14 09:42:02 -04007117 * Draw the HTML page into the specified canvas. This call ignores any
7118 * view-specific zoom, scroll offset, or other changes. It does not draw
7119 * any view-specific chrome, such as progress or URL bars.
7120 *
7121 * @hide only needs to be accessible to Browser and testing
7122 */
7123 public void drawPage(Canvas canvas) {
7124 mWebViewCore.drawContentPicture(canvas, 0, false, false);
7125 }
7126
7127 /**
Ben Murdochecbc65c2010-01-13 10:54:56 +00007128 * Set the time to wait between passing touches to WebCore. See also the
7129 * TOUCH_SENT_INTERVAL member for further discussion.
7130 *
7131 * @hide This is only used by the DRT test application.
7132 */
7133 public void setTouchInterval(int interval) {
7134 mCurrentTouchInterval = interval;
7135 }
7136
7137 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007138 * Update our cache with updatedText.
7139 * @param updatedText The new text to put in our cache.
7140 */
7141 /* package */ void updateCachedTextfield(String updatedText) {
7142 // Also place our generation number so that when we look at the cache
7143 // we recognize that it is up to date.
7144 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
7145 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007146
Cary Clark1cb97ee2009-12-11 12:10:36 -05007147 private native int nativeCacheHitFramePointer();
7148 private native Rect nativeCacheHitNodeBounds();
7149 private native int nativeCacheHitNodePointer();
Leon Scroggins4890feb2009-07-02 10:37:10 -04007150 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007151 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04007152 private native int nativeCursorFramePointer();
7153 private native Rect nativeCursorNodeBounds();
Cary Clarkaffa5d22010-01-07 12:18:23 -05007154 private native int nativeCursorNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04007155 /* package */ native boolean nativeCursorMatchesFocus();
7156 private native boolean nativeCursorIntersects(Rect visibleRect);
7157 private native boolean nativeCursorIsAnchor();
7158 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04007159 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04007160 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007161 /**
7162 * Returns true if the native cursor node says it wants to handle key events
7163 * (ala plugins). This can only be called if mNativeClass is non-zero!
7164 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04007165 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007166 private native void nativeDebugDump();
7167 private native void nativeDestroy();
Cary Clark2ec30692010-02-23 10:50:38 -05007168 private native boolean nativeEvaluateLayersAnimations();
7169 private native void nativeDrawExtras(Canvas canvas, int extra);
Cary Clarkd6982c92009-05-29 11:02:22 -04007170 private native void nativeDumpDisplayTree(String urlOrNull);
7171 private native int nativeFindAll(String findLower, String findUpper);
7172 private native void nativeFindNext(boolean forward);
Leon Scroggins3a503392010-01-06 17:04:38 -05007173 /* package */ native int nativeFocusCandidateFramePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007174 private native boolean nativeFocusCandidateIsPassword();
7175 private native boolean nativeFocusCandidateIsRtlText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007176 private native boolean nativeFocusCandidateIsTextInput();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05007177 /* package */ native int nativeFocusCandidateMaxLength();
Leon Scroggins0ca70882009-06-26 17:45:29 -04007178 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007179 private native Rect nativeFocusCandidateNodeBounds();
Cary Clarkaffa5d22010-01-07 12:18:23 -05007180 private native int nativeFocusCandidatePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007181 private native String nativeFocusCandidateText();
7182 private native int nativeFocusCandidateTextSize();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05007183 /**
7184 * Returns an integer corresponding to WebView.cpp::type.
7185 * See WebTextView.setType()
7186 */
7187 private native int nativeFocusCandidateType();
Derek Sollenberger718d69f2009-10-19 15:56:43 -04007188 private native boolean nativeFocusIsPlugin();
Cary Clarke84a0db2010-03-17 15:58:20 -04007189 private native Rect nativeFocusNodeBounds();
Leon Scroggins4890feb2009-07-02 10:37:10 -04007190 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04007191 private native Rect nativeGetCursorRingBounds();
Cary Clark57d2c3a2009-12-23 13:57:15 -05007192 private native String nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04007193 private native boolean nativeHasCursorNode();
7194 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04007195 private native void nativeHideCursor();
Cary Clarkd6982c92009-05-29 11:02:22 -04007196 private native String nativeImageURI(int x, int y);
7197 private native void nativeInstrumentReport();
Leon Scroggins1be40982010-03-01 11:20:31 -05007198 /* package */ native boolean nativeMoveCursorToNextTextInput();
Cary Clarkd6982c92009-05-29 11:02:22 -04007199 // return true if the page has been scrolled
7200 private native boolean nativeMotionUp(int x, int y, int slop);
7201 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007202 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04007203 boolean noScroll);
7204 private native int nativeMoveGeneration();
7205 private native void nativeMoveSelection(int x, int y,
7206 boolean extendSelection);
Cary Clark1cb97ee2009-12-11 12:10:36 -05007207 private native boolean nativePointInNavCache(int x, int y, int slop);
Cary Clarkd6982c92009-05-29 11:02:22 -04007208 // Like many other of our native methods, you must make sure that
7209 // mNativeClass is not null before calling this method.
7210 private native void nativeRecordButtons(boolean focused,
7211 boolean pressed, boolean invalidate);
7212 private native void nativeSelectBestAt(Rect rect);
Cary Clarkde023c12010-03-03 10:05:16 -05007213 private native void nativeSetFindIsEmpty();
7214 private native void nativeSetFindIsUp(boolean isUp);
Cary Clarkd6982c92009-05-29 11:02:22 -04007215 private native void nativeSetFollowedLink(boolean followed);
7216 private native void nativeSetHeightCanMeasure(boolean measure);
Cary Clark855dd9e2010-01-27 17:03:05 -05007217 private native void nativeSetRootLayer(int layer);
Cary Clark2ec30692010-02-23 10:50:38 -05007218 private native void nativeSetSelectionPointer(boolean set,
7219 float scale, int x, int y, boolean extendSelection);
7220 private native void nativeSetSelectionRegion(boolean set);
Cary Clark31b83672010-03-09 09:20:34 -05007221 private native Rect nativeSubtractLayers(Rect content);
Cary Clarkd6982c92009-05-29 11:02:22 -04007222 private native int nativeTextGeneration();
7223 // Never call this version except by updateCachedTextfield(String) -
7224 // we always want to pass in our generation number.
7225 private native void nativeUpdateCachedTextfield(String updatedText,
7226 int generation);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07007227 // return NO_LEFTEDGE means failure.
7228 private static final int NO_LEFTEDGE = -1;
Cary Clark77d98f42009-07-31 09:40:38 -04007229 private native int nativeGetBlockLeftEdge(int x, int y, float scale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007230}