blob: 825ea464186d5e8f151acb36b93ff5e8e553a7b5 [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;
Cary Clarkd6982c92009-05-29 11:02:22 -0400538
Grace Klobac2242f22010-03-05 14:00:26 -0800539 private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
540 private static final int LAST_PACKAGE_MSG_ID = FIND_AGAIN;
541
542 static final String[] HandlerPrivateDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400543 "REMEMBER_PASSWORD", // = 1;
544 "NEVER_REMEMBER_PASSWORD", // = 2;
545 "SWITCH_TO_SHORTPRESS", // = 3;
546 "SWITCH_TO_LONGPRESS", // = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700547 "RELEASE_SINGLE_TAP", // = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400548 "REQUEST_FORM_DATA", // = 6;
Grace Kloba96949ef2010-01-25 09:53:01 -0800549 "RESUME_WEBCORE_PRIORITY", // = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400550 "DRAG_HELD_MOTIONLESS", // = 8;
551 "AWAKEN_SCROLL_BARS", // = 9;
Grace Klobac2242f22010-03-05 14:00:26 -0800552 "PREVENT_DEFAULT_TIMEOUT" // = 10;
553 };
554
555 static final String[] HandlerPackageDebugString = {
556 "SCROLL_TO_MSG_ID", // = 101;
557 "SCROLL_BY_MSG_ID", // = 102;
558 "SPAWN_SCROLL_TO_MSG_ID", // = 103;
559 "SYNC_SCROLL_TO_MSG_ID", // = 104;
560 "NEW_PICTURE_MSG_ID", // = 105;
561 "UPDATE_TEXT_ENTRY_MSG_ID", // = 106;
562 "WEBCORE_INITIALIZED_MSG_ID", // = 107;
563 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 108;
564 "UPDATE_ZOOM_RANGE", // = 109;
565 "MOVE_OUT_OF_PLUGIN", // = 110;
566 "CLEAR_TEXT_ENTRY", // = 111;
567 "UPDATE_TEXT_SELECTION_MSG_ID", // = 112;
568 "SHOW_RECT_MSG_ID", // = 113;
569 "LONG_PRESS_CENTER", // = 114;
570 "PREVENT_TOUCH_ID", // = 115;
571 "WEBCORE_NEED_TOUCH_EVENTS", // = 116;
572 "INVAL_RECT_MSG_ID", // = 117;
573 "REQUEST_KEYBOARD", // = 118;
574 "DO_MOTION_UP", // = 119;
575 "SHOW_FULLSCREEN", // = 120;
576 "HIDE_FULLSCREEN", // = 121;
577 "DOM_FOCUS_CHANGED", // = 122;
578 "IMMEDIATE_REPAINT_MSG_ID", // = 123;
579 "SET_ROOT_LAYER_MSG_ID", // = 124;
580 "RETURN_LABEL", // = 125;
581 "FIND_AGAIN" // = 126;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800582 };
583
Grace Klobaa4fa1072009-11-09 12:01:50 -0800584 // If the site doesn't use the viewport meta tag to specify the viewport,
585 // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
586 static final int DEFAULT_VIEWPORT_WIDTH = 800;
587
588 // normally we try to fit the content to the minimum preferred width
589 // calculated by the Webkit. To avoid the bad behavior when some site's
590 // minimum preferred width keeps growing when changing the viewport width or
591 // the minimum preferred width is huge, an upper limit is needed.
592 static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
593
Grace Kloba25737912009-06-19 12:42:47 -0700594 // default scale limit. Depending on the display density
595 private static float DEFAULT_MAX_ZOOM_SCALE;
596 private static float DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 // scale limit, which can be set through viewport meta tag in the web page
Grace Kloba25737912009-06-19 12:42:47 -0700598 private float mMaxZoomScale;
599 private float mMinZoomScale;
Grace Klobae397a882009-08-06 12:04:14 -0700600 private boolean mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601
602 // initial scale in percent. 0 means using default.
Grace Kloba16efce72009-11-10 15:49:03 -0800603 private int mInitialScaleInPercent = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800604
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700605 // while in the zoom overview mode, the page's width is fully fit to the
606 // current window. The page is alive, in another words, you can click to
607 // follow the links. Double tap will toggle between zoom overview mode and
608 // the last zoom scale.
609 boolean mInZoomOverview = false;
Leon Scrogginsf58ffac2009-08-13 15:34:06 -0400610
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700611 // ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
612 // engadget always have wider mContentWidth no matter what viewport size is.
Grace Klobaa4fa1072009-11-09 12:01:50 -0800613 int mZoomOverviewWidth = DEFAULT_VIEWPORT_WIDTH;
Grace Kloba3a0def22010-01-23 21:11:54 -0800614 float mTextWrapScale;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700615
Grace Kloba25737912009-06-19 12:42:47 -0700616 // default scale. Depending on the display density.
617 static int DEFAULT_SCALE_PERCENT;
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700618 private float mDefaultScale;
Grace Kloba25737912009-06-19 12:42:47 -0700619
Grace Klobac6f95fe2010-03-10 13:25:34 -0800620 private static float MINIMUM_SCALE_INCREMENT = 0.01f;
621
Grace Kloba3a0def22010-01-23 21:11:54 -0800622 // set to true temporarily during ScaleGesture triggered zoom
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 private boolean mPreviewZoomOnly = false;
624
625 // computed scale and inverse, from mZoomWidth.
Grace Kloba25737912009-06-19 12:42:47 -0700626 private float mActualScale;
627 private float mInvActualScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800628 // if this is non-zero, it is used on drawing rather than mActualScale
629 private float mZoomScale;
630 private float mInvInitialZoomScale;
631 private float mInvFinalZoomScale;
Grace Kloba675c7d22009-07-23 09:21:21 -0700632 private int mInitialScrollX;
633 private int mInitialScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634 private long mZoomStart;
635 private static final int ZOOM_ANIMATION_LENGTH = 500;
636
637 private boolean mUserScroll = false;
638
639 private int mSnapScrollMode = SNAP_NONE;
Cary Clarkac492e12009-10-14 14:53:37 -0400640 private static final int SNAP_NONE = 0;
641 private static final int SNAP_LOCK = 1; // not a separate state
642 private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
643 private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400645
Cary Clark2ec30692010-02-23 10:50:38 -0500646 // keep these in sync with their counterparts in WebView.cpp
647 private static final int DRAW_EXTRAS_NONE = 0;
648 private static final int DRAW_EXTRAS_FIND = 1;
649 private static final int DRAW_EXTRAS_SELECTION = 2;
650 private static final int DRAW_EXTRAS_CURSOR_RING = 3;
651
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 // Used to match key downs and key ups
653 private boolean mGotKeyDown;
654
655 /* package */ static boolean mLogEvent = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656
657 // for event log
658 private long mLastTouchUpTime = 0;
659
660 /**
661 * URI scheme for telephone number
662 */
663 public static final String SCHEME_TEL = "tel:";
664 /**
665 * URI scheme for email address
666 */
667 public static final String SCHEME_MAILTO = "mailto:";
668 /**
669 * URI scheme for map address
670 */
671 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400672
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 private int mBackgroundColor = Color.WHITE;
674
675 // Used to notify listeners of a new picture.
676 private PictureListener mPictureListener;
677 /**
678 * Interface to listen for new pictures as they change.
679 */
680 public interface PictureListener {
681 /**
682 * Notify the listener that the picture has changed.
683 * @param view The WebView that owns the picture.
684 * @param picture The new picture.
685 */
686 public void onNewPicture(WebView view, Picture picture);
687 }
688
Leon Scroggins3246c222009-05-26 09:51:23 -0400689 // FIXME: Want to make this public, but need to change the API file.
690 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 /**
692 * Default HitTestResult, where the target is unknown
693 */
694 public static final int UNKNOWN_TYPE = 0;
695 /**
696 * HitTestResult for hitting a HTML::a tag
697 */
698 public static final int ANCHOR_TYPE = 1;
699 /**
700 * HitTestResult for hitting a phone number
701 */
702 public static final int PHONE_TYPE = 2;
703 /**
704 * HitTestResult for hitting a map address
705 */
706 public static final int GEO_TYPE = 3;
707 /**
708 * HitTestResult for hitting an email address
709 */
710 public static final int EMAIL_TYPE = 4;
711 /**
712 * HitTestResult for hitting an HTML::img tag
713 */
714 public static final int IMAGE_TYPE = 5;
715 /**
716 * HitTestResult for hitting a HTML::a tag which contains HTML::img
717 */
718 public static final int IMAGE_ANCHOR_TYPE = 6;
719 /**
720 * HitTestResult for hitting a HTML::a tag with src=http
721 */
722 public static final int SRC_ANCHOR_TYPE = 7;
723 /**
724 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
725 */
726 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
727 /**
728 * HitTestResult for hitting an edit text area
729 */
730 public static final int EDIT_TEXT_TYPE = 9;
731
732 private int mType;
733 private String mExtra;
734
735 HitTestResult() {
736 mType = UNKNOWN_TYPE;
737 }
738
739 private void setType(int type) {
740 mType = type;
741 }
742
743 private void setExtra(String extra) {
744 mExtra = extra;
745 }
746
747 public int getType() {
748 return mType;
749 }
750
751 public String getExtra() {
752 return mExtra;
753 }
754 }
755
The Android Open Source Project10592532009-03-18 17:39:46 -0700756 // The View containing the zoom controls
757 private ExtendedZoomControls mZoomControls;
758 private Runnable mZoomControlRunnable;
759
Cary Clarkd6982c92009-05-29 11:02:22 -0400760 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700762 // These keep track of the center point of the zoom. They are used to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800763 // determine the point around which we should zoom.
764 private float mZoomCenterX;
765 private float mZoomCenterY;
766
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700767 private ZoomButtonsController.OnZoomListener mZoomListener =
768 new ZoomButtonsController.OnZoomListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800769
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800770 public void onVisibilityChanged(boolean visible) {
771 if (visible) {
772 switchOutDrawHistory();
Mike Reed8b302092009-11-12 12:50:20 -0500773 // Bring back the hidden zoom controls.
774 mZoomButtonsController.getZoomControls().setVisibility(
775 View.VISIBLE);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700776 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 }
778 }
779
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700780 public void onZoom(boolean zoomIn) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800781 if (zoomIn) {
782 zoomIn();
783 } else {
784 zoomOut();
785 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400786
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700787 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 };
Cary Clarkd6982c92009-05-29 11:02:22 -0400790
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 /**
792 * Construct a new WebView with a Context object.
793 * @param context A Context object used to access application assets.
794 */
795 public WebView(Context context) {
796 this(context, null);
797 }
798
799 /**
800 * Construct a new WebView with layout parameters.
801 * @param context A Context object used to access application assets.
802 * @param attrs An AttributeSet passed to our parent.
803 */
804 public WebView(Context context, AttributeSet attrs) {
805 this(context, attrs, com.android.internal.R.attr.webViewStyle);
806 }
807
808 /**
809 * Construct a new WebView with layout parameters and a default style.
810 * @param context A Context object used to access application assets.
811 * @param attrs An AttributeSet passed to our parent.
812 * @param defStyle The default style resource ID.
813 */
814 public WebView(Context context, AttributeSet attrs, int defStyle) {
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100815 this(context, attrs, defStyle, null);
816 }
817
818 /**
819 * Construct a new WebView with layout parameters, a default style and a set
820 * of custom Javscript interfaces to be added to the WebView at initialization
Romain Guy01d0fbf2009-12-01 14:52:19 -0800821 * time. This guarantees that these interfaces will be available when the JS
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100822 * context is initialized.
823 * @param context A Context object used to access application assets.
824 * @param attrs An AttributeSet passed to our parent.
825 * @param defStyle The default style resource ID.
826 * @param javascriptInterfaces is a Map of intareface names, as keys, and
827 * object implementing those interfaces, as values.
828 * @hide pending API council approval.
829 */
830 protected WebView(Context context, AttributeSet attrs, int defStyle,
831 Map<String, Object> javascriptInterfaces) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 super(context, attrs, defStyle);
833 init();
834
835 mCallbackProxy = new CallbackProxy(context, this);
Grace Kloba9a67c822009-12-20 11:33:58 -0800836 mViewManager = new ViewManager(this);
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100837 mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javascriptInterfaces);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 mDatabase = WebViewDatabase.getInstance(context);
Grace Klobad7625dd2010-03-04 11:46:12 -0800839 mScroller = new OverScroller(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700840
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700841 mZoomButtonsController = new ZoomButtonsController(this);
842 mZoomButtonsController.setOnZoomListener(mZoomListener);
Leon Scrogginsaa3f96a2009-06-11 14:46:35 -0400843 // ZoomButtonsController positions the buttons at the bottom, but in
844 // the middle. Change their layout parameters so they appear on the
845 // right.
846 View controls = mZoomButtonsController.getZoomControls();
847 ViewGroup.LayoutParams params = controls.getLayoutParams();
848 if (params instanceof FrameLayout.LayoutParams) {
849 FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
850 params;
851 frameParams.gravity = Gravity.RIGHT;
852 }
Grace Kloba3a0def22010-01-23 21:11:54 -0800853 updateMultiTouchSupport(context);
854 }
855
856 void updateMultiTouchSupport(Context context) {
857 WebSettings settings = getSettings();
858 mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
859 PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
860 && settings.supportZoom() && settings.getBuiltInZoomControls();
861 if (mSupportMultiTouch && (mScaleDetector == null)) {
862 mScaleDetector = new ScaleGestureDetector(context,
863 new ScaleDetectorListener());
864 } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
865 mScaleDetector = null;
866 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700867 }
868
869 private void updateZoomButtonsEnabled() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700870 boolean canZoomIn = mActualScale < mMaxZoomScale;
Grace Kloba455e3af2009-08-13 11:01:21 -0700871 boolean canZoomOut = mActualScale > mMinZoomScale && !mInZoomOverview;
The Android Open Source Project10592532009-03-18 17:39:46 -0700872 if (!canZoomIn && !canZoomOut) {
873 // Hide the zoom in and out buttons, as well as the fit to page
874 // button, if the page cannot zoom
875 mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700876 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -0700877 // Set each one individually, as a page may be able to zoom in
878 // or out.
879 mZoomButtonsController.setZoomInEnabled(canZoomIn);
880 mZoomButtonsController.setZoomOutEnabled(canZoomOut);
The Android Open Source Project10592532009-03-18 17:39:46 -0700881 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 }
883
884 private void init() {
885 setWillNotDraw(false);
886 setFocusable(true);
887 setFocusableInTouchMode(true);
888 setClickable(true);
889 setLongClickable(true);
890
Romain Guy4296fc42009-07-06 11:48:52 -0700891 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700892 int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 mTouchSlopSquare = slop * slop;
894 mMinLockSnapReverseDistance = slop;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700895 slop = configuration.getScaledDoubleTapSlop();
896 mDoubleTapSlopSquare = slop * slop;
Grace Kloba25737912009-06-19 12:42:47 -0700897 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700898 // use one line height, 16 based on our current default font, for how
899 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -0700900 mNavSlop = (int) (16 * density);
901 // density adjusted scale factors
902 DEFAULT_SCALE_PERCENT = (int) (100 * density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700903 mDefaultScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700904 mActualScale = density;
905 mInvActualScale = 1 / density;
Grace Kloba3a0def22010-01-23 21:11:54 -0800906 mTextWrapScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700907 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
908 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
909 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
910 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
Romain Guy4296fc42009-07-06 11:48:52 -0700911 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800912 }
913
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700914 /* package */void updateDefaultZoomDensity(int zoomDensity) {
915 final float density = getContext().getResources().getDisplayMetrics().density
916 * 100 / zoomDensity;
917 if (Math.abs(density - mDefaultScale) > 0.01) {
918 float scaleFactor = density / mDefaultScale;
919 // adjust the limits
920 mNavSlop = (int) (16 * density);
921 DEFAULT_SCALE_PERCENT = (int) (100 * density);
922 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
923 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
924 mDefaultScale = density;
925 mMaxZoomScale *= scaleFactor;
926 mMinZoomScale *= scaleFactor;
Grace Kloba3a0def22010-01-23 21:11:54 -0800927 setNewZoomScale(mActualScale * scaleFactor, true, false);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700928 }
929 }
930
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 /* package */ boolean onSavePassword(String schemePlusHost, String username,
932 String password, final Message resumeMsg) {
933 boolean rVal = false;
934 if (resumeMsg == null) {
935 // null resumeMsg implies saving password silently
936 mDatabase.setUsernamePassword(schemePlusHost, username, password);
937 } else {
938 final Message remember = mPrivateHandler.obtainMessage(
939 REMEMBER_PASSWORD);
940 remember.getData().putString("host", schemePlusHost);
941 remember.getData().putString("username", username);
942 remember.getData().putString("password", password);
943 remember.obj = resumeMsg;
944
945 final Message neverRemember = mPrivateHandler.obtainMessage(
946 NEVER_REMEMBER_PASSWORD);
947 neverRemember.getData().putString("host", schemePlusHost);
948 neverRemember.getData().putString("username", username);
949 neverRemember.getData().putString("password", password);
950 neverRemember.obj = resumeMsg;
951
952 new AlertDialog.Builder(getContext())
953 .setTitle(com.android.internal.R.string.save_password_label)
954 .setMessage(com.android.internal.R.string.save_password_message)
955 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
956 new DialogInterface.OnClickListener() {
957 public void onClick(DialogInterface dialog, int which) {
958 resumeMsg.sendToTarget();
959 }
960 })
961 .setNeutralButton(com.android.internal.R.string.save_password_remember,
962 new DialogInterface.OnClickListener() {
963 public void onClick(DialogInterface dialog, int which) {
964 remember.sendToTarget();
965 }
966 })
967 .setNegativeButton(com.android.internal.R.string.save_password_never,
968 new DialogInterface.OnClickListener() {
969 public void onClick(DialogInterface dialog, int which) {
970 neverRemember.sendToTarget();
971 }
972 })
973 .setOnCancelListener(new OnCancelListener() {
974 public void onCancel(DialogInterface dialog) {
975 resumeMsg.sendToTarget();
976 }
977 }).show();
978 // Return true so that WebViewCore will pause while the dialog is
979 // up.
980 rVal = true;
981 }
982 return rVal;
983 }
984
985 @Override
986 public void setScrollBarStyle(int style) {
987 if (style == View.SCROLLBARS_INSIDE_INSET
988 || style == View.SCROLLBARS_OUTSIDE_INSET) {
989 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
990 } else {
991 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
992 }
993 super.setScrollBarStyle(style);
994 }
995
996 /**
997 * Specify whether the horizontal scrollbar has overlay style.
998 * @param overlay TRUE if horizontal scrollbar should have overlay style.
999 */
1000 public void setHorizontalScrollbarOverlay(boolean overlay) {
1001 mOverlayHorizontalScrollbar = overlay;
1002 }
1003
1004 /**
1005 * Specify whether the vertical scrollbar has overlay style.
1006 * @param overlay TRUE if vertical scrollbar should have overlay style.
1007 */
1008 public void setVerticalScrollbarOverlay(boolean overlay) {
1009 mOverlayVerticalScrollbar = overlay;
1010 }
1011
1012 /**
1013 * Return whether horizontal scrollbar has overlay style
1014 * @return TRUE if horizontal scrollbar has overlay style.
1015 */
1016 public boolean overlayHorizontalScrollbar() {
1017 return mOverlayHorizontalScrollbar;
1018 }
1019
1020 /**
1021 * Return whether vertical scrollbar has overlay style
1022 * @return TRUE if vertical scrollbar has overlay style.
1023 */
1024 public boolean overlayVerticalScrollbar() {
1025 return mOverlayVerticalScrollbar;
1026 }
1027
1028 /*
1029 * Return the width of the view where the content of WebView should render
1030 * to.
Grace Kloba6ed525e2009-09-17 15:31:12 -07001031 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 */
Grace Kloba6ed525e2009-09-17 15:31:12 -07001033 /* package */ int getViewWidth() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
1035 return getWidth();
1036 } else {
1037 return getWidth() - getVerticalScrollbarWidth();
1038 }
1039 }
1040
1041 /*
Mike Reede8853fc2009-09-04 14:01:48 -04001042 * returns the height of the titlebarview (if any). Does not care about
1043 * scrolling
1044 */
1045 private int getTitleHeight() {
1046 return mTitleBar != null ? mTitleBar.getHeight() : 0;
1047 }
1048
1049 /*
1050 * Return the amount of the titlebarview (if any) that is visible
1051 */
1052 private int getVisibleTitleHeight() {
Grace Klobad7625dd2010-03-04 11:46:12 -08001053 // need to restrict mScrollY due to over scroll
1054 return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04001055 }
1056
1057 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 * Return the height of the view where the content of WebView should render
Leon Scroggins0236e672009-09-02 21:12:08 -04001059 * to. Note that this excludes mTitleBar, if there is one.
Grace Kloba6ed525e2009-09-17 15:31:12 -07001060 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 */
Grace Kloba6ed525e2009-09-17 15:31:12 -07001062 /* package */ int getViewHeight() {
Grace Kloba8eff73f2009-09-24 09:34:32 -07001063 return getViewHeightWithTitle() - getVisibleTitleHeight();
1064 }
1065
1066 private int getViewHeightWithTitle() {
Leon Scroggins0236e672009-09-02 21:12:08 -04001067 int height = getHeight();
Mike Reede8853fc2009-09-04 14:01:48 -04001068 if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001069 height -= getHorizontalScrollbarHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 }
Grace Kloba8eff73f2009-09-24 09:34:32 -07001071 return height;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 }
1073
1074 /**
1075 * @return The SSL certificate for the main top-level page or null if
1076 * there is no certificate (the site is not secure).
1077 */
1078 public SslCertificate getCertificate() {
1079 return mCertificate;
1080 }
1081
1082 /**
1083 * Sets the SSL certificate for the main top-level page.
1084 */
1085 public void setCertificate(SslCertificate certificate) {
1086 // here, the certificate can be null (if the site is not secure)
1087 mCertificate = certificate;
1088 }
1089
1090 //-------------------------------------------------------------------------
1091 // Methods called by activity
1092 //-------------------------------------------------------------------------
1093
1094 /**
1095 * Save the username and password for a particular host in the WebView's
1096 * internal database.
1097 * @param host The host that required the credentials.
1098 * @param username The username for the given host.
1099 * @param password The password for the given host.
1100 */
1101 public void savePassword(String host, String username, String password) {
1102 mDatabase.setUsernamePassword(host, username, password);
1103 }
1104
1105 /**
1106 * Set the HTTP authentication credentials for a given host and realm.
1107 *
1108 * @param host The host for the credentials.
1109 * @param realm The realm for the credentials.
1110 * @param username The username for the password. If it is null, it means
1111 * password can't be saved.
1112 * @param password The password
1113 */
1114 public void setHttpAuthUsernamePassword(String host, String realm,
1115 String username, String password) {
1116 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
1117 }
1118
1119 /**
1120 * Retrieve the HTTP authentication username and password for a given
1121 * host & realm pair
1122 *
1123 * @param host The host for which the credentials apply.
1124 * @param realm The realm for which the credentials apply.
1125 * @return String[] if found, String[0] is username, which can be null and
1126 * String[1] is password. Return null if it can't find anything.
1127 */
1128 public String[] getHttpAuthUsernamePassword(String host, String realm) {
1129 return mDatabase.getHttpAuthUsernamePassword(host, realm);
1130 }
1131
1132 /**
1133 * Destroy the internal state of the WebView. This method should be called
1134 * after the WebView has been removed from the view system. No other
1135 * methods may be called on a WebView after destroy.
1136 */
1137 public void destroy() {
Leon Scroggins6088e832010-02-17 13:17:32 -05001138 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 if (mWebViewCore != null) {
1140 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -04001141 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 mCallbackProxy.setWebViewClient(null);
1143 mCallbackProxy.setWebChromeClient(null);
1144 // Tell WebViewCore to destroy itself
Andrei Popescu04098572010-03-09 12:23:19 +00001145 synchronized (this) {
1146 WebViewCore webViewCore = mWebViewCore;
1147 mWebViewCore = null; // prevent using partial webViewCore
1148 webViewCore.destroy();
1149 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 // Remove any pending messages that might not be serviced yet.
1151 mPrivateHandler.removeCallbacksAndMessages(null);
1152 mCallbackProxy.removeCallbacksAndMessages(null);
1153 // Wake up the WebCore thread just in case it is waiting for a
1154 // javascript dialog.
1155 synchronized (mCallbackProxy) {
1156 mCallbackProxy.notify();
1157 }
1158 }
1159 if (mNativeClass != 0) {
1160 nativeDestroy();
1161 mNativeClass = 0;
1162 }
1163 }
1164
1165 /**
1166 * Enables platform notifications of data state and proxy changes.
1167 */
1168 public static void enablePlatformNotifications() {
1169 Network.enablePlatformNotifications();
1170 }
1171
1172 /**
1173 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001174 * from the Activity's onPause() or onStop().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001175 */
1176 public static void disablePlatformNotifications() {
1177 Network.disablePlatformNotifications();
1178 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001179
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001180 /**
Feng Qianb3081372009-06-29 15:55:18 -07001181 * Sets JavaScript engine flags.
1182 *
1183 * @param flags JS engine flags in a String
1184 *
1185 * @hide pending API solidification
1186 */
1187 public void setJsFlags(String flags) {
1188 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1189 }
1190
1191 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192 * Inform WebView of the network state. This is used to set
1193 * the javascript property window.navigator.isOnline and
1194 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1195 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001196 */
1197 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001198 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1199 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 }
1201
1202 /**
Andrei Popescuf5dba882010-01-12 22:42:41 +00001203 * Inform WebView about the current network type.
1204 * {@hide}
1205 */
1206 public void setNetworkType(String type, String subtype) {
1207 Map<String, String> map = new HashMap<String, String>();
1208 map.put("type", type);
1209 map.put("subtype", subtype);
1210 mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
1211 }
1212 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001213 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1215 * method no longer stores the display data for this WebView. The previous
1216 * behavior could potentially leak files if {@link #restoreState} was never
1217 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1218 * and restoring the display data.
1219 * @param outState The Bundle to store the WebView state.
1220 * @return The same copy of the back/forward list used to save the state. If
1221 * saveState fails, the returned list will be null.
1222 * @see #savePicture
1223 * @see #restorePicture
1224 */
1225 public WebBackForwardList saveState(Bundle outState) {
1226 if (outState == null) {
1227 return null;
1228 }
1229 // We grab a copy of the back/forward list because a client of WebView
1230 // may have invalidated the history list by calling clearHistory.
1231 WebBackForwardList list = copyBackForwardList();
1232 final int currentIndex = list.getCurrentIndex();
1233 final int size = list.getSize();
1234 // We should fail saving the state if the list is empty or the index is
1235 // not in a valid range.
1236 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1237 return null;
1238 }
1239 outState.putInt("index", currentIndex);
1240 // FIXME: This should just be a byte[][] instead of ArrayList but
1241 // Parcel.java does not have the code to handle multi-dimensional
1242 // arrays.
1243 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1244 for (int i = 0; i < size; i++) {
1245 WebHistoryItem item = list.getItemAtIndex(i);
Cary Clark4fbf81b2009-09-29 16:07:56 -04001246 if (null == item) {
1247 // FIXME: this shouldn't happen
1248 // need to determine how item got set to null
1249 Log.w(LOGTAG, "saveState: Unexpected null history item.");
1250 return null;
1251 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001252 byte[] data = item.getFlattenedData();
1253 if (data == null) {
1254 // It would be very odd to not have any data for a given history
1255 // item. And we will fail to rebuild the history list without
1256 // flattened data.
1257 return null;
1258 }
1259 history.add(data);
1260 }
1261 outState.putSerializable("history", history);
1262 if (mCertificate != null) {
1263 outState.putBundle("certificate",
1264 SslCertificate.saveState(mCertificate));
1265 }
1266 return list;
1267 }
1268
1269 /**
1270 * Save the current display data to the Bundle given. Used in conjunction
1271 * with {@link #saveState}.
1272 * @param b A Bundle to store the display data.
1273 * @param dest The file to store the serialized picture data. Will be
1274 * overwritten with this WebView's picture data.
1275 * @return True if the picture was successfully saved.
1276 */
1277 public boolean savePicture(Bundle b, File dest) {
1278 if (dest == null || b == null) {
1279 return false;
1280 }
1281 final Picture p = capturePicture();
1282 try {
1283 final FileOutputStream out = new FileOutputStream(dest);
1284 p.writeToStream(out);
1285 out.close();
Mike Reed189e7352010-03-05 10:12:29 -05001286 // now update the bundle
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001287 b.putInt("scrollX", mScrollX);
1288 b.putInt("scrollY", mScrollY);
1289 b.putFloat("scale", mActualScale);
Grace Kloba3a0def22010-01-23 21:11:54 -08001290 b.putFloat("textwrapScale", mTextWrapScale);
1291 b.putBoolean("overview", mInZoomOverview);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001292 return true;
Mike Reed189e7352010-03-05 10:12:29 -05001293 } catch (FileNotFoundException e){
1294 e.printStackTrace();
1295 } catch (IOException e) {
1296 e.printStackTrace();
1297 } catch (RuntimeException e) {
1298 e.printStackTrace();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 }
1300 return false;
1301 }
1302
1303 /**
1304 * Restore the display data that was save in {@link #savePicture}. Used in
1305 * conjunction with {@link #restoreState}.
1306 * @param b A Bundle containing the saved display data.
1307 * @param src The file where the picture data was stored.
1308 * @return True if the picture was successfully restored.
1309 */
1310 public boolean restorePicture(Bundle b, File src) {
1311 if (src == null || b == null) {
1312 return false;
1313 }
1314 if (src.exists()) {
1315 Picture p = null;
1316 try {
1317 final FileInputStream in = new FileInputStream(src);
1318 p = Picture.createFromStream(in);
1319 in.close();
1320 } catch (FileNotFoundException e){
1321 e.printStackTrace();
1322 } catch (RuntimeException e) {
1323 e.printStackTrace();
1324 } catch (IOException e) {
1325 e.printStackTrace();
1326 }
1327 if (p != null) {
1328 int sx = b.getInt("scrollX", 0);
1329 int sy = b.getInt("scrollY", 0);
1330 float scale = b.getFloat("scale", 1.0f);
1331 mDrawHistory = true;
1332 mHistoryPicture = p;
1333 mScrollX = sx;
1334 mScrollY = sy;
1335 mHistoryWidth = Math.round(p.getWidth() * scale);
1336 mHistoryHeight = Math.round(p.getHeight() * scale);
1337 // as getWidth() / getHeight() of the view are not
1338 // available yet, set up mActualScale, so that when
1339 // onSizeChanged() is called, the rest will be set
1340 // correctly
1341 mActualScale = scale;
Cary Clark2ec30692010-02-23 10:50:38 -05001342 mInvActualScale = 1 / scale;
Grace Kloba3a0def22010-01-23 21:11:54 -08001343 mTextWrapScale = b.getFloat("textwrapScale", scale);
1344 mInZoomOverview = b.getBoolean("overview");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345 invalidate();
1346 return true;
1347 }
1348 }
1349 return false;
1350 }
1351
1352 /**
1353 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001354 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1355 * be called to restore the state of the WebView before using the object. If
1356 * it is called after the WebView has had a chance to build state (load
1357 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 * side-effects. Please note that this method no longer restores the
1359 * display data for this WebView. See {@link #savePicture} and {@link
1360 * #restorePicture} for saving and restoring the display data.
1361 * @param inState The incoming Bundle of state.
1362 * @return The restored back/forward list or null if restoreState failed.
1363 * @see #savePicture
1364 * @see #restorePicture
1365 */
1366 public WebBackForwardList restoreState(Bundle inState) {
1367 WebBackForwardList returnList = null;
1368 if (inState == null) {
1369 return returnList;
1370 }
1371 if (inState.containsKey("index") && inState.containsKey("history")) {
1372 mCertificate = SslCertificate.restoreState(
1373 inState.getBundle("certificate"));
1374
1375 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1376 final int index = inState.getInt("index");
1377 // We can't use a clone of the list because we need to modify the
1378 // shared copy, so synchronize instead to prevent concurrent
1379 // modifications.
1380 synchronized (list) {
1381 final List<byte[]> history =
1382 (List<byte[]>) inState.getSerializable("history");
1383 final int size = history.size();
1384 // Check the index bounds so we don't crash in native code while
1385 // restoring the history index.
1386 if (index < 0 || index >= size) {
1387 return null;
1388 }
1389 for (int i = 0; i < size; i++) {
1390 byte[] data = history.remove(0);
1391 if (data == null) {
1392 // If we somehow have null data, we cannot reconstruct
1393 // the item and thus our history list cannot be rebuilt.
1394 return null;
1395 }
1396 WebHistoryItem item = new WebHistoryItem(data);
1397 list.addHistoryItem(item);
1398 }
1399 // Grab the most recent copy to return to the caller.
1400 returnList = copyBackForwardList();
1401 // Update the copy to have the correct index.
1402 returnList.setCurrentIndex(index);
1403 }
1404 // Remove all pending messages because we are restoring previous
1405 // state.
1406 mWebViewCore.removeMessages();
1407 // Send a restore state message.
1408 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1409 }
1410 return returnList;
1411 }
1412
1413 /**
Grace Klobad0d9bc22010-01-26 18:08:28 -08001414 * Load the given url with the extra headers.
1415 * @param url The url of the resource to load.
1416 * @param extraHeaders The extra headers sent with this url. This should not
1417 * include the common headers like "user-agent". If it does, it
1418 * will be replaced by the intrinsic value of the WebView.
1419 */
1420 public void loadUrl(String url, Map<String, String> extraHeaders) {
1421 switchOutDrawHistory();
1422 WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
1423 arg.mUrl = url;
1424 arg.mExtraHeaders = extraHeaders;
1425 mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
Leon Scroggins6088e832010-02-17 13:17:32 -05001426 clearTextEntry(false);
Grace Klobad0d9bc22010-01-26 18:08:28 -08001427 }
1428
1429 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001430 * Load the given url.
1431 * @param url The url of the resource to load.
1432 */
1433 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001434 if (url == null) {
1435 return;
1436 }
Grace Klobad0d9bc22010-01-26 18:08:28 -08001437 loadUrl(url, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001438 }
1439
1440 /**
Grace Kloba57534302009-05-22 18:55:02 -07001441 * Load the url with postData using "POST" method into the WebView. If url
1442 * is not a network url, it will be loaded with {link
1443 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001444 *
Grace Kloba57534302009-05-22 18:55:02 -07001445 * @param url The url of the resource to load.
1446 * @param postData The data will be passed to "POST" request.
Grace Kloba57534302009-05-22 18:55:02 -07001447 */
1448 public void postUrl(String url, byte[] postData) {
1449 if (URLUtil.isNetworkUrl(url)) {
1450 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001451 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1452 arg.mUrl = url;
1453 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001454 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
Leon Scroggins6088e832010-02-17 13:17:32 -05001455 clearTextEntry(false);
Grace Kloba57534302009-05-22 18:55:02 -07001456 } else {
1457 loadUrl(url);
1458 }
1459 }
1460
1461 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001462 * Load the given data into the WebView. This will load the data into
1463 * WebView using the data: scheme. Content loaded through this mechanism
1464 * does not have the ability to load content from the network.
1465 * @param data A String of data in the given encoding.
1466 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1467 * @param encoding The encoding of the data. i.e. utf-8, base64
1468 */
1469 public void loadData(String data, String mimeType, String encoding) {
1470 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1471 }
1472
1473 /**
1474 * Load the given data into the WebView, use the provided URL as the base
1475 * URL for the content. The base URL is the URL that represents the page
1476 * that is loaded through this interface. As such, it is used for the
1477 * history entry and to resolve any relative URLs. The failUrl is used if
1478 * browser fails to load the data provided. If it is empty or null, and the
1479 * load fails, then no history entry is created.
1480 * <p>
1481 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1482 * files through "file:///android_asset/" for the sub resources is more
1483 * restricted. If you provide null or empty string as baseUrl, you won't be
1484 * able to access asset files. If the baseUrl is anything other than
1485 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1486 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001487 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 * @param baseUrl Url to resolve relative paths with, if null defaults to
1489 * "about:blank"
1490 * @param data A String of data in the given encoding.
1491 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1492 * defaults to "text/html"
1493 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
1494 * @param failUrl URL to use if the content fails to load or null.
1495 */
1496 public void loadDataWithBaseURL(String baseUrl, String data,
1497 String mimeType, String encoding, String failUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001498
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001499 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1500 loadData(data, mimeType, encoding);
1501 return;
1502 }
1503 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001504 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1505 arg.mBaseUrl = baseUrl;
1506 arg.mData = data;
1507 arg.mMimeType = mimeType;
1508 arg.mEncoding = encoding;
1509 arg.mFailUrl = failUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001510 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
Leon Scroggins6088e832010-02-17 13:17:32 -05001511 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001512 }
1513
1514 /**
1515 * Stop the current load.
1516 */
1517 public void stopLoading() {
1518 // TODO: should we clear all the messages in the queue before sending
1519 // STOP_LOADING?
1520 switchOutDrawHistory();
1521 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1522 }
1523
1524 /**
1525 * Reload the current url.
1526 */
1527 public void reload() {
Leon Scroggins6088e832010-02-17 13:17:32 -05001528 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529 switchOutDrawHistory();
1530 mWebViewCore.sendMessage(EventHub.RELOAD);
1531 }
1532
1533 /**
1534 * Return true if this WebView has a back history item.
1535 * @return True iff this WebView has a back history item.
1536 */
1537 public boolean canGoBack() {
1538 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1539 synchronized (l) {
1540 if (l.getClearPending()) {
1541 return false;
1542 } else {
1543 return l.getCurrentIndex() > 0;
1544 }
1545 }
1546 }
1547
1548 /**
1549 * Go back in the history of this WebView.
1550 */
1551 public void goBack() {
1552 goBackOrForward(-1);
1553 }
1554
1555 /**
1556 * Return true if this WebView has a forward history item.
1557 * @return True iff this Webview has a forward history item.
1558 */
1559 public boolean canGoForward() {
1560 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1561 synchronized (l) {
1562 if (l.getClearPending()) {
1563 return false;
1564 } else {
1565 return l.getCurrentIndex() < l.getSize() - 1;
1566 }
1567 }
1568 }
1569
1570 /**
1571 * Go forward in the history of this WebView.
1572 */
1573 public void goForward() {
1574 goBackOrForward(1);
1575 }
1576
1577 /**
1578 * Return true if the page can go back or forward the given
1579 * number of steps.
1580 * @param steps The negative or positive number of steps to move the
1581 * history.
1582 */
1583 public boolean canGoBackOrForward(int steps) {
1584 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1585 synchronized (l) {
1586 if (l.getClearPending()) {
1587 return false;
1588 } else {
1589 int newIndex = l.getCurrentIndex() + steps;
1590 return newIndex >= 0 && newIndex < l.getSize();
1591 }
1592 }
1593 }
1594
1595 /**
1596 * Go to the history item that is the number of steps away from
1597 * the current item. Steps is negative if backward and positive
1598 * if forward.
1599 * @param steps The number of steps to take back or forward in the back
1600 * forward list.
1601 */
1602 public void goBackOrForward(int steps) {
1603 goBackOrForward(steps, false);
1604 }
1605
1606 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
1607 // every time we go back or forward, we want to reset the
1608 // WebView certificate:
1609 // if the new site is secure, we will reload it and get a
1610 // new certificate set;
1611 // if the new site is not secure, the certificate must be
1612 // null, and that will be the case
1613 mCertificate = null;
1614 if (steps != 0) {
Leon Scroggins6088e832010-02-17 13:17:32 -05001615 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001616 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
1617 ignoreSnapshot ? 1 : 0);
1618 }
1619 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001620
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001621 private boolean extendScroll(int y) {
1622 int finalY = mScroller.getFinalY();
1623 int newY = pinLocY(finalY + y);
1624 if (newY == finalY) return false;
1625 mScroller.setFinalY(newY);
1626 mScroller.extendDuration(computeDuration(0, y));
1627 return true;
1628 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001629
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001630 /**
1631 * Scroll the contents of the view up by half the view size
1632 * @param top true to jump to the top of the page
1633 * @return true if the page was scrolled
1634 */
1635 public boolean pageUp(boolean top) {
1636 if (mNativeClass == 0) {
1637 return false;
1638 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001639 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001640 if (top) {
1641 // go to the top of the document
1642 return pinScrollTo(mScrollX, 0, true, 0);
1643 }
1644 // Page up
1645 int h = getHeight();
1646 int y;
1647 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1648 y = -h + PAGE_SCROLL_OVERLAP;
1649 } else {
1650 y = -h / 2;
1651 }
1652 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001653 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001654 : extendScroll(y);
1655 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001656
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001657 /**
1658 * Scroll the contents of the view down by half the page size
1659 * @param bottom true to jump to bottom of page
1660 * @return true if the page was scrolled
1661 */
1662 public boolean pageDown(boolean bottom) {
1663 if (mNativeClass == 0) {
1664 return false;
1665 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001666 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001667 if (bottom) {
Grace Klobad7439f42009-11-10 14:11:33 -08001668 return pinScrollTo(mScrollX, computeVerticalScrollRange(), true, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001669 }
1670 // Page down.
1671 int h = getHeight();
1672 int y;
1673 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1674 y = h - PAGE_SCROLL_OVERLAP;
1675 } else {
1676 y = h / 2;
1677 }
1678 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001679 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001680 : extendScroll(y);
1681 }
1682
1683 /**
1684 * Clear the view so that onDraw() will draw nothing but white background,
1685 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
1686 */
1687 public void clearView() {
1688 mContentWidth = 0;
1689 mContentHeight = 0;
1690 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
1691 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001692
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 /**
1694 * Return a new picture that captures the current display of the webview.
1695 * This is a copy of the display, and will be unaffected if the webview
1696 * later loads a different URL.
1697 *
1698 * @return a picture containing the current contents of the view. Note this
1699 * picture is of the entire document, and is not restricted to the
1700 * bounds of the view.
1701 */
1702 public Picture capturePicture() {
Cary Clarkd6982c92009-05-29 11:02:22 -04001703 if (null == mWebViewCore) return null; // check for out of memory tab
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001704 return mWebViewCore.copyContentPicture();
1705 }
1706
1707 /**
1708 * Return true if the browser is displaying a TextView for text input.
1709 */
1710 private boolean inEditingMode() {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001711 return mWebTextView != null && mWebTextView.getParent() != null
1712 && mWebTextView.hasFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001713 }
1714
Leon Scroggins6088e832010-02-17 13:17:32 -05001715 /**
1716 * Remove the WebTextView.
1717 * @param disableFocusController If true, send a message to webkit
1718 * disabling the focus controller, so the caret stops blinking.
1719 */
1720 private void clearTextEntry(boolean disableFocusController) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001721 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001722 mWebTextView.remove();
Leon Scroggins6088e832010-02-17 13:17:32 -05001723 if (disableFocusController) {
1724 setFocusControllerInactive();
1725 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001726 }
1727 }
1728
Cary Clarkd6982c92009-05-29 11:02:22 -04001729 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001730 * Return the current scale of the WebView
1731 * @return The current scale.
1732 */
1733 public float getScale() {
1734 return mActualScale;
1735 }
1736
1737 /**
1738 * Set the initial scale for the WebView. 0 means default. If
1739 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
1740 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
1741 * WebView starts will this value as initial scale.
1742 *
1743 * @param scaleInPercent The initial scale in percent.
1744 */
1745 public void setInitialScale(int scaleInPercent) {
Grace Kloba16efce72009-11-10 15:49:03 -08001746 mInitialScaleInPercent = scaleInPercent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001747 }
1748
1749 /**
1750 * Invoke the graphical zoom picker widget for this WebView. This will
1751 * result in the zoom widget appearing on the screen to control the zoom
1752 * level of this WebView.
1753 */
1754 public void invokeZoomPicker() {
1755 if (!getSettings().supportZoom()) {
1756 Log.w(LOGTAG, "This WebView doesn't support zoom.");
1757 return;
1758 }
Leon Scroggins6088e832010-02-17 13:17:32 -05001759 clearTextEntry(false);
The Android Open Source Project10592532009-03-18 17:39:46 -07001760 if (getSettings().getBuiltInZoomControls()) {
1761 mZoomButtonsController.setVisible(true);
1762 } else {
1763 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
1764 mPrivateHandler.postDelayed(mZoomControlRunnable,
1765 ZOOM_CONTROLS_TIMEOUT);
1766 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001767 }
1768
1769 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001770 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001771 * is found and the anchor has a non-javascript url, the HitTestResult type
1772 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
1773 * anchor does not have a url or if it is a javascript url, the type will
1774 * be UNKNOWN_TYPE and the url has to be retrieved through
1775 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
1776 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
1777 * the "extra" field. A type of
1778 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
1779 * a child node. If a phone number is found, the HitTestResult type is set
1780 * to PHONE_TYPE and the phone number is set in the "extra" field of
1781 * HitTestResult. If a map address is found, the HitTestResult type is set
1782 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
1783 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
1784 * and the email is set in the "extra" field of HitTestResult. Otherwise,
1785 * HitTestResult type is set to UNKNOWN_TYPE.
1786 */
1787 public HitTestResult getHitTestResult() {
1788 if (mNativeClass == 0) {
1789 return null;
1790 }
1791
1792 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04001793 if (nativeHasCursorNode()) {
1794 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001795 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04001796 } else {
1797 String text = nativeCursorText();
1798 if (text != null) {
1799 if (text.startsWith(SCHEME_TEL)) {
1800 result.setType(HitTestResult.PHONE_TYPE);
1801 result.setExtra(text.substring(SCHEME_TEL.length()));
1802 } else if (text.startsWith(SCHEME_MAILTO)) {
1803 result.setType(HitTestResult.EMAIL_TYPE);
1804 result.setExtra(text.substring(SCHEME_MAILTO.length()));
1805 } else if (text.startsWith(SCHEME_GEO)) {
1806 result.setType(HitTestResult.GEO_TYPE);
1807 result.setExtra(URLDecoder.decode(text
1808 .substring(SCHEME_GEO.length())));
1809 } else if (nativeCursorIsAnchor()) {
1810 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
1811 result.setExtra(text);
1812 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001813 }
1814 }
1815 }
1816 int type = result.getType();
1817 if (type == HitTestResult.UNKNOWN_TYPE
1818 || type == HitTestResult.SRC_ANCHOR_TYPE) {
1819 // Now check to see if it is an image.
Leon Scroggins0236e672009-09-02 21:12:08 -04001820 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1821 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 String text = nativeImageURI(contentX, contentY);
1823 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001824 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
1825 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001826 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
1827 result.setExtra(text);
1828 }
1829 }
1830 return result;
1831 }
1832
Leon Scrogginse26efa32009-12-15 16:38:45 -05001833 // Called by JNI when the DOM has changed the focus. Clear the focus so
1834 // that new keys will go to the newly focused field
1835 private void domChangedFocus() {
1836 if (inEditingMode()) {
1837 mPrivateHandler.obtainMessage(DOM_FOCUS_CHANGED).sendToTarget();
1838 }
1839 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001840 /**
1841 * Request the href of an anchor element due to getFocusNodePath returning
1842 * "href." If hrefMsg is null, this method returns immediately and does not
1843 * dispatch hrefMsg to its target.
Cary Clarkd6982c92009-05-29 11:02:22 -04001844 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001845 * @param hrefMsg This message will be dispatched with the result of the
1846 * request as the data member with "url" as key. The result can
1847 * be null.
1848 */
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001849 // FIXME: API change required to change the name of this function. We now
1850 // look at the cursor node, and not the focus node. Also, what is
1851 // getFocusNodePath?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001852 public void requestFocusNodeHref(Message hrefMsg) {
1853 if (hrefMsg == null || mNativeClass == 0) {
1854 return;
1855 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001856 if (nativeCursorIsAnchor()) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001857 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
Cary Clarkd6982c92009-05-29 11:02:22 -04001858 nativeCursorFramePointer(), nativeCursorNodePointer(),
1859 hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001860 }
1861 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001862
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001863 /**
1864 * Request the url of the image last touched by the user. msg will be sent
1865 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04001866 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001867 * @param msg This message will be dispatched with the result of the request
1868 * as the data member with "url" as key. The result can be null.
1869 */
1870 public void requestImageRef(Message msg) {
Cary Clark7f970112009-10-15 15:29:08 -04001871 if (0 == mNativeClass) return; // client isn't initialized
Leon Scroggins0236e672009-09-02 21:12:08 -04001872 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
1873 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001874 String ref = nativeImageURI(contentX, contentY);
1875 Bundle data = msg.getData();
1876 data.putString("url", ref);
1877 msg.setData(data);
1878 msg.sendToTarget();
1879 }
1880
1881 private static int pinLoc(int x, int viewMax, int docMax) {
1882// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
1883 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001884 // pin the short document to the top/left of the screen
1885 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001886// Log.d(LOGTAG, "--- center " + x);
1887 } else if (x < 0) {
1888 x = 0;
1889// Log.d(LOGTAG, "--- zero");
1890 } else if (x + viewMax > docMax) {
1891 x = docMax - viewMax;
1892// Log.d(LOGTAG, "--- pin " + x);
1893 }
1894 return x;
1895 }
1896
1897 // Expects x in view coordinates
1898 private int pinLocX(int x) {
Grace Klobad7625dd2010-03-04 11:46:12 -08001899 if (mInOverScrollMode) return x;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001900 return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
1901 }
1902
1903 // Expects y in view coordinates
1904 private int pinLocY(int y) {
Grace Klobad7625dd2010-03-04 11:46:12 -08001905 if (mInOverScrollMode) return y;
Mike Reede8853fc2009-09-04 14:01:48 -04001906 int titleH = getTitleHeight();
1907 // if the titlebar is still visible, just pin against 0
1908 if (y <= titleH) {
1909 return Math.max(y, 0);
1910 }
1911 // convert to 0-based coordinate (subtract the title height)
1912 // pin(), and then add the title height back in
1913 return pinLoc(y - titleH, getViewHeight(),
1914 computeVerticalScrollRange()) + titleH;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001915 }
1916
Leon Scroggins0236e672009-09-02 21:12:08 -04001917 /**
1918 * A title bar which is embedded in this WebView, and scrolls along with it
1919 * vertically, but not horizontally.
1920 */
1921 private View mTitleBar;
1922
1923 /**
Leon Scroggins58992ea2009-09-17 15:55:31 -04001924 * Since we draw the title bar ourselves, we removed the shadow from the
1925 * browser's activity. We do want a shadow at the bottom of the title bar,
1926 * or at the top of the screen if the title bar is not visible. This
1927 * drawable serves that purpose.
1928 */
1929 private Drawable mTitleShadow;
1930
1931 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001932 * Add or remove a title bar to be embedded into the WebView, and scroll
1933 * along with it vertically, while remaining in view horizontally. Pass
1934 * null to remove the title bar from the WebView, and return to drawing
1935 * the WebView normally without translating to account for the title bar.
1936 * @hide
1937 */
Leon Scroggins078c52c2009-09-04 16:58:09 -04001938 public void setEmbeddedTitleBar(View v) {
1939 if (mTitleBar == v) return;
1940 if (mTitleBar != null) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001941 removeView(mTitleBar);
Leon Scroggins078c52c2009-09-04 16:58:09 -04001942 }
1943 if (null != v) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001944 addView(v, new AbsoluteLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08001945 ViewGroup.LayoutParams.MATCH_PARENT,
Leon Scroggins078c52c2009-09-04 16:58:09 -04001946 ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
Leon Scroggins58992ea2009-09-17 15:55:31 -04001947 if (mTitleShadow == null) {
1948 mTitleShadow = (Drawable) mContext.getResources().getDrawable(
1949 com.android.internal.R.drawable.title_bar_shadow);
1950 }
Leon Scroggins0236e672009-09-02 21:12:08 -04001951 }
1952 mTitleBar = v;
1953 }
1954
1955 /**
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001956 * Given a distance in view space, convert it to content space. Note: this
1957 * does not reflect translation, just scaling, so this should not be called
1958 * with coordinates, but should be called for dimensions like width or
1959 * height.
1960 */
1961 private int viewToContentDimension(int d) {
1962 return Math.round(d * mInvActualScale);
1963 }
1964
1965 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04001966 * Given an x coordinate in view space, convert it to content space. Also
1967 * may be used for absolute heights (such as for the WebTextView's
1968 * textSize, which is unaffected by the height of the title bar).
1969 */
1970 /*package*/ int viewToContentX(int x) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001971 return viewToContentDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001972 }
1973
Leon Scroggins0236e672009-09-02 21:12:08 -04001974 /**
1975 * Given a y coordinate in view space, convert it to content space.
1976 * Takes into account the height of the title bar if there is one
1977 * embedded into the WebView.
1978 */
1979 /*package*/ int viewToContentY(int y) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001980 return viewToContentDimension(y - getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04001981 }
1982
1983 /**
1984 * Given a distance in content space, convert it to view space. Note: this
1985 * does not reflect translation, just scaling, so this should not be called
1986 * with coordinates, but should be called for dimensions like width or
1987 * height.
1988 */
1989 /*package*/ int contentToViewDimension(int d) {
1990 return Math.round(d * mActualScale);
Leon Scroggins0236e672009-09-02 21:12:08 -04001991 }
1992
1993 /**
1994 * Given an x coordinate in content space, convert it to view
Leon Scrogginsd3997e52009-09-21 14:15:18 -04001995 * space.
Leon Scroggins0236e672009-09-02 21:12:08 -04001996 */
1997 /*package*/ int contentToViewX(int x) {
Mike Reede8853fc2009-09-04 14:01:48 -04001998 return contentToViewDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001999 }
2000
Leon Scroggins0236e672009-09-02 21:12:08 -04002001 /**
2002 * Given a y coordinate in content space, convert it to view
2003 * space. Takes into account the height of the title bar.
2004 */
2005 /*package*/ int contentToViewY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04002006 return contentToViewDimension(y) + getTitleHeight();
Leon Scroggins0236e672009-09-02 21:12:08 -04002007 }
2008
Mike Reede9e86b82009-09-15 11:26:53 -04002009 private Rect contentToViewRect(Rect x) {
2010 return new Rect(contentToViewX(x.left), contentToViewY(x.top),
2011 contentToViewX(x.right), contentToViewY(x.bottom));
2012 }
2013
2014 /* To invalidate a rectangle in content coordinates, we need to transform
2015 the rect into view coordinates, so we can then call invalidate(...).
2016
2017 Normally, we would just call contentToView[XY](...), which eventually
2018 calls Math.round(coordinate * mActualScale). However, for invalidates,
2019 we need to account for the slop that occurs with antialiasing. To
2020 address that, we are a little more liberal in the size of the rect that
2021 we invalidate.
2022
2023 This liberal calculation calls floor() for the top/left, and ceil() for
2024 the bottom/right coordinates. This catches the possible extra pixels of
2025 antialiasing that we might have missed with just round().
2026 */
2027
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002028 // Called by JNI to invalidate the View, given rectangle coordinates in
2029 // content space
2030 private void viewInvalidate(int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04002031 final float scale = mActualScale;
2032 final int dy = getTitleHeight();
2033 invalidate((int)Math.floor(l * scale),
2034 (int)Math.floor(t * scale) + dy,
2035 (int)Math.ceil(r * scale),
2036 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002037 }
2038
2039 // Called by JNI to invalidate the View after a delay, given rectangle
2040 // coordinates in content space
2041 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
Mike Reede9e86b82009-09-15 11:26:53 -04002042 final float scale = mActualScale;
2043 final int dy = getTitleHeight();
2044 postInvalidateDelayed(delay,
2045 (int)Math.floor(l * scale),
2046 (int)Math.floor(t * scale) + dy,
2047 (int)Math.ceil(r * scale),
2048 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002049 }
2050
Mike Reede9e86b82009-09-15 11:26:53 -04002051 private void invalidateContentRect(Rect r) {
2052 viewInvalidate(r.left, r.top, r.right, r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002053 }
2054
Cary Clark278ce052009-08-31 16:08:42 -04002055 // stop the scroll animation, and don't let a subsequent fling add
2056 // to the existing velocity
2057 private void abortAnimation() {
2058 mScroller.abortAnimation();
2059 mLastVelocity = 0;
2060 }
2061
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002062 /* call from webcoreview.draw(), so we're still executing in the UI thread
2063 */
2064 private void recordNewContentSize(int w, int h, boolean updateLayout) {
2065
2066 // premature data from webkit, ignore
2067 if ((w | h) == 0) {
2068 return;
2069 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002070
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002071 // don't abort a scroll animation if we didn't change anything
2072 if (mContentWidth != w || mContentHeight != h) {
2073 // record new dimensions
2074 mContentWidth = w;
2075 mContentHeight = h;
2076 // If history Picture is drawn, don't update scroll. They will be
2077 // updated when we get out of that mode.
2078 if (!mDrawHistory) {
2079 // repin our scroll, taking into account the new content size
2080 int oldX = mScrollX;
2081 int oldY = mScrollY;
2082 mScrollX = pinLocX(mScrollX);
2083 mScrollY = pinLocY(mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002084 if (oldX != mScrollX || oldY != mScrollY) {
2085 sendOurVisibleRect();
2086 }
Leon Scrogginsd84e7d52009-09-29 11:11:45 -04002087 if (!mScroller.isFinished()) {
2088 // We are in the middle of a scroll. Repin the final scroll
2089 // position.
2090 mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
2091 mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
2092 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 }
2094 }
2095 contentSizeChanged(updateLayout);
2096 }
2097
Grace Kloba3a0def22010-01-23 21:11:54 -08002098 private void setNewZoomScale(float scale, boolean updateTextWrapScale,
2099 boolean force) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002100 if (scale < mMinZoomScale) {
2101 scale = mMinZoomScale;
Grace Klobab3d0cc52010-03-01 12:25:29 -08002102 // set mInZoomOverview for non mobile sites
2103 if (scale < mDefaultScale) mInZoomOverview = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002104 } else if (scale > mMaxZoomScale) {
2105 scale = mMaxZoomScale;
2106 }
Grace Kloba3a0def22010-01-23 21:11:54 -08002107 if (updateTextWrapScale) {
2108 mTextWrapScale = scale;
2109 // reset mLastHeightSent to force VIEW_SIZE_CHANGED sent to WebKit
2110 mLastHeightSent = 0;
2111 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002112 if (scale != mActualScale || force) {
2113 if (mDrawHistory) {
2114 // If history Picture is drawn, don't update scroll. They will
2115 // be updated when we get out of that mode.
2116 if (scale != mActualScale && !mPreviewZoomOnly) {
2117 mCallbackProxy.onScaleChanged(mActualScale, scale);
2118 }
2119 mActualScale = scale;
2120 mInvActualScale = 1 / scale;
Grace Kloba3a0def22010-01-23 21:11:54 -08002121 sendViewSizeZoom();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002122 } else {
2123 // update our scroll so we don't appear to jump
2124 // i.e. keep the center of the doc in the center of the view
2125
2126 int oldX = mScrollX;
2127 int oldY = mScrollY;
2128 float ratio = scale * mInvActualScale; // old inverse
2129 float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
Grace Klobac0c03af2009-09-17 11:01:44 -07002130 float sy = ratio * oldY + (ratio - 1)
2131 * (mZoomCenterY - getTitleHeight());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002132
2133 // now update our new scale and inverse
2134 if (scale != mActualScale && !mPreviewZoomOnly) {
2135 mCallbackProxy.onScaleChanged(mActualScale, scale);
2136 }
2137 mActualScale = scale;
2138 mInvActualScale = 1 / scale;
2139
Patrick Scott0a5ce012009-07-02 08:56:10 -04002140 // Scale all the child views
2141 mViewManager.scaleAll();
2142
Cary Clarkd6982c92009-05-29 11:02:22 -04002143 // as we don't have animation for scaling, don't do animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002144 // for scrolling, as it causes weird intermediate state
2145 // pinScrollTo(Math.round(sx), Math.round(sy));
2146 mScrollX = pinLocX(Math.round(sx));
2147 mScrollY = pinLocY(Math.round(sy));
2148
Grace Kloba3a0def22010-01-23 21:11:54 -08002149 // update webkit
2150 sendViewSizeZoom();
2151 sendOurVisibleRect();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002152 }
2153 }
2154 }
2155
2156 // Used to avoid sending many visible rect messages.
2157 private Rect mLastVisibleRectSent;
2158 private Rect mLastGlobalRect;
2159
2160 private Rect sendOurVisibleRect() {
Grace Kloba3a0def22010-01-23 21:11:54 -08002161 if (mPreviewZoomOnly) return mLastVisibleRectSent;
2162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002163 Rect rect = new Rect();
2164 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002165 // Rect.equals() checks for null input.
2166 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04002167 Point pos = new Point(rect.left, rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002168 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Cary Clarked56eda2009-06-18 09:48:47 -04002169 nativeMoveGeneration(), 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002170 mLastVisibleRectSent = rect;
2171 }
2172 Rect globalRect = new Rect();
2173 if (getGlobalVisibleRect(globalRect)
2174 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04002175 if (DebugFlags.WEB_VIEW) {
2176 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
2177 + globalRect.top + ",r=" + globalRect.right + ",b="
2178 + globalRect.bottom);
2179 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002180 // TODO: the global offset is only used by windowRect()
2181 // in ChromeClientAndroid ; other clients such as touch
2182 // and mouse events could return view + screen relative points.
2183 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
2184 mLastGlobalRect = globalRect;
2185 }
2186 return rect;
2187 }
2188
2189 // Sets r to be the visible rectangle of our webview in view coordinates
2190 private void calcOurVisibleRect(Rect r) {
2191 Point p = new Point();
2192 getGlobalVisibleRect(r, p);
2193 r.offset(-p.x, -p.y);
Cary Clark3524be92009-06-22 13:09:11 -04002194 if (mFindIsUp) {
Cary Clark5bb6b522009-09-21 11:58:31 -04002195 r.bottom -= mFindHeight;
Cary Clark3524be92009-06-22 13:09:11 -04002196 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002197 }
2198
2199 // Sets r to be our visible rectangle in content coordinates
2200 private void calcOurContentVisibleRect(Rect r) {
2201 calcOurVisibleRect(r);
Mike Reed180403a2010-03-10 11:34:26 -05002202 // since we might overscroll, pin the rect to the bounds of the content
2203 r.left = Math.max(viewToContentX(r.left), 0);
Leon Scroggins37df6a82009-09-23 10:31:23 -04002204 // viewToContentY will remove the total height of the title bar. Add
2205 // the visible height back in to account for the fact that if the title
2206 // bar is partially visible, the part of the visible rect which is
2207 // displaying our content is displaced by that amount.
Mike Reed180403a2010-03-10 11:34:26 -05002208 r.top = Math.max(viewToContentY(r.top + getVisibleTitleHeight()), 0);
2209 r.right = Math.min(viewToContentX(r.right), mContentWidth);
2210 r.bottom = Math.min(viewToContentY(r.bottom), mContentHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002211 }
2212
Grace Klobaef347ef2009-07-30 11:20:32 -07002213 static class ViewSizeData {
2214 int mWidth;
2215 int mHeight;
2216 int mTextWrapWidth;
Grace Kloba3a0def22010-01-23 21:11:54 -08002217 int mAnchorX;
2218 int mAnchorY;
Grace Klobaef347ef2009-07-30 11:20:32 -07002219 float mScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002220 boolean mIgnoreHeight;
Grace Klobaef347ef2009-07-30 11:20:32 -07002221 }
2222
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002223 /**
2224 * Compute unzoomed width and height, and if they differ from the last
2225 * values we sent, send them to webkit (to be used has new viewport)
2226 *
2227 * @return true if new values were sent
2228 */
2229 private boolean sendViewSizeZoom() {
Grace Kloba3a0def22010-01-23 21:11:54 -08002230 if (mPreviewZoomOnly) return false;
2231
Grace Klobaef347ef2009-07-30 11:20:32 -07002232 int viewWidth = getViewWidth();
2233 int newWidth = Math.round(viewWidth * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002234 int newHeight = Math.round(getViewHeight() * mInvActualScale);
2235 /*
2236 * Because the native side may have already done a layout before the
2237 * View system was able to measure us, we have to send a height of 0 to
2238 * remove excess whitespace when we grow our width. This will trigger a
2239 * layout and a change in content size. This content size change will
2240 * mean that contentSizeChanged will either call this method directly or
2241 * indirectly from onSizeChanged.
2242 */
2243 if (newWidth > mLastWidthSent && mWrapContent) {
2244 newHeight = 0;
2245 }
2246 // Avoid sending another message if the dimensions have not changed.
2247 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
Grace Klobaef347ef2009-07-30 11:20:32 -07002248 ViewSizeData data = new ViewSizeData();
2249 data.mWidth = newWidth;
2250 data.mHeight = newHeight;
Grace Kloba3a0def22010-01-23 21:11:54 -08002251 data.mTextWrapWidth = Math.round(viewWidth / mTextWrapScale);;
Grace Klobaef347ef2009-07-30 11:20:32 -07002252 data.mScale = mActualScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002253 data.mIgnoreHeight = mZoomScale != 0 && !mHeightCanMeasure;
Grace Kloba3a0def22010-01-23 21:11:54 -08002254 data.mAnchorX = mAnchorX;
2255 data.mAnchorY = mAnchorY;
Grace Klobaef347ef2009-07-30 11:20:32 -07002256 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002257 mLastWidthSent = newWidth;
2258 mLastHeightSent = newHeight;
Grace Kloba3a0def22010-01-23 21:11:54 -08002259 mAnchorX = mAnchorY = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002260 return true;
2261 }
2262 return false;
2263 }
2264
2265 @Override
2266 protected int computeHorizontalScrollRange() {
2267 if (mDrawHistory) {
2268 return mHistoryWidth;
2269 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002270 // to avoid rounding error caused unnecessary scrollbar, use floor
2271 return (int) Math.floor(mContentWidth * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002272 }
2273 }
2274
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002275 @Override
2276 protected int computeVerticalScrollRange() {
2277 if (mDrawHistory) {
2278 return mHistoryHeight;
2279 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002280 // to avoid rounding error caused unnecessary scrollbar, use floor
2281 return (int) Math.floor(mContentHeight * mActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002282 }
2283 }
2284
Leon Scroggins0236e672009-09-02 21:12:08 -04002285 @Override
2286 protected int computeVerticalScrollOffset() {
Mike Reede8853fc2009-09-04 14:01:48 -04002287 return Math.max(mScrollY - getTitleHeight(), 0);
Leon Scroggins0236e672009-09-02 21:12:08 -04002288 }
2289
2290 @Override
2291 protected int computeVerticalScrollExtent() {
2292 return getViewHeight();
2293 }
2294
Mike Reede8853fc2009-09-04 14:01:48 -04002295 /** @hide */
2296 @Override
2297 protected void onDrawVerticalScrollBar(Canvas canvas,
2298 Drawable scrollBar,
2299 int l, int t, int r, int b) {
2300 scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
2301 scrollBar.draw(canvas);
2302 }
2303
Grace Klobad7625dd2010-03-04 11:46:12 -08002304 @Override
2305 protected void onOverscrolled(int scrollX, int scrollY, boolean clampedX,
2306 boolean clampedY) {
2307 mInOverScrollMode = false;
2308 int maxX = computeMaxScrollX();
Grace Klobac6f95fe2010-03-10 13:25:34 -08002309 if (maxX == 0 && (Math.abs(mMinZoomScale - mMaxZoomScale)
2310 < MINIMUM_SCALE_INCREMENT)
Grace Kloba2d17e1d2010-03-09 10:55:04 -08002311 || !getSettings().supportZoom()
2312 || !getSettings().getUseWideViewPort()) {
2313 // do not over scroll x if the page just fits the screen and it
2314 // can't zoom or the view doesn't use wide viewport
Grace Klobad7625dd2010-03-04 11:46:12 -08002315 scrollX = pinLocX(scrollX);
2316 } else if (scrollX < 0 || scrollX > maxX) {
2317 mInOverScrollMode = true;
2318 }
2319 if (scrollY < 0 || scrollY > computeMaxScrollY()) {
2320 mInOverScrollMode = true;
2321 }
2322 super.scrollTo(scrollX, scrollY);
2323 }
2324
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002325 /**
2326 * Get the url for the current page. This is not always the same as the url
2327 * passed to WebViewClient.onPageStarted because although the load for
2328 * that url has begun, the current page may not have changed.
2329 * @return The url for the current page.
2330 */
2331 public String getUrl() {
2332 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2333 return h != null ? h.getUrl() : null;
2334 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002335
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002336 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002337 * Get the original url for the current page. This is not always the same
2338 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002339 * load for that url has begun, the current page may not have changed.
2340 * Also, there may have been redirects resulting in a different url to that
2341 * originally requested.
2342 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002343 */
2344 public String getOriginalUrl() {
2345 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2346 return h != null ? h.getOriginalUrl() : null;
2347 }
2348
2349 /**
2350 * Get the title for the current page. This is the title of the current page
2351 * until WebViewClient.onReceivedTitle is called.
2352 * @return The title for the current page.
2353 */
2354 public String getTitle() {
2355 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2356 return h != null ? h.getTitle() : null;
2357 }
2358
2359 /**
2360 * Get the favicon for the current page. This is the favicon of the current
2361 * page until WebViewClient.onReceivedIcon is called.
2362 * @return The favicon for the current page.
2363 */
2364 public Bitmap getFavicon() {
2365 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2366 return h != null ? h.getFavicon() : null;
2367 }
2368
2369 /**
Patrick Scott2ba12622009-08-04 13:20:05 -04002370 * Get the touch icon url for the apple-touch-icon <link> element.
2371 * @hide
2372 */
2373 public String getTouchIconUrl() {
2374 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2375 return h != null ? h.getTouchIconUrl() : null;
2376 }
2377
2378 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002379 * Get the progress for the current page.
2380 * @return The progress for the current page between 0 and 100.
2381 */
2382 public int getProgress() {
2383 return mCallbackProxy.getProgress();
2384 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002385
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002386 /**
2387 * @return the height of the HTML content.
2388 */
2389 public int getContentHeight() {
2390 return mContentHeight;
2391 }
2392
2393 /**
Leon Scrogginsea96d1e2009-09-23 13:41:01 -04002394 * @return the width of the HTML content.
2395 * @hide
2396 */
2397 public int getContentWidth() {
2398 return mContentWidth;
2399 }
2400
2401 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002402 * Pause all layout, parsing, and javascript timers for all webviews. This
2403 * is a global requests, not restricted to just this webview. This can be
2404 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002405 */
2406 public void pauseTimers() {
2407 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
2408 }
2409
2410 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002411 * Resume all layout, parsing, and javascript timers for all webviews.
2412 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002413 */
2414 public void resumeTimers() {
2415 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
2416 }
2417
2418 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04002419 * Call this to pause any extra processing associated with this view and
2420 * its associated DOM/plugins/javascript/etc. For example, if the view is
2421 * taken offscreen, this could be called to reduce unnecessary CPU and/or
2422 * network traffic. When the view is again "active", call onResume().
2423 *
2424 * Note that this differs from pauseTimers(), which affects all views/DOMs
2425 * @hide
2426 */
2427 public void onPause() {
2428 if (!mIsPaused) {
2429 mIsPaused = true;
2430 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
2431 }
2432 }
2433
2434 /**
2435 * Call this to balanace a previous call to onPause()
2436 * @hide
2437 */
2438 public void onResume() {
2439 if (mIsPaused) {
2440 mIsPaused = false;
2441 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2442 }
2443 }
2444
2445 /**
2446 * Returns true if the view is paused, meaning onPause() was called. Calling
2447 * onResume() sets the paused state back to false.
2448 * @hide
2449 */
2450 public boolean isPaused() {
2451 return mIsPaused;
2452 }
2453
2454 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002455 * Call this to inform the view that memory is low so that it can
2456 * free any available memory.
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002457 */
2458 public void freeMemory() {
2459 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2460 }
2461
2462 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002463 * Clear the resource cache. Note that the cache is per-application, so
2464 * this will clear the cache for all WebViews used.
2465 *
2466 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002467 */
2468 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002469 // Note: this really needs to be a static method as it clears cache for all
2470 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2471 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002472 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2473 includeDiskFiles ? 1 : 0, 0);
2474 }
2475
2476 /**
2477 * Make sure that clearing the form data removes the adapter from the
2478 * currently focused textfield if there is one.
2479 */
2480 public void clearFormData() {
2481 if (inEditingMode()) {
2482 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002483 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002484 }
2485 }
2486
2487 /**
2488 * Tell the WebView to clear its internal back/forward list.
2489 */
2490 public void clearHistory() {
2491 mCallbackProxy.getBackForwardList().setClearPending();
2492 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2493 }
2494
2495 /**
2496 * Clear the SSL preferences table stored in response to proceeding with SSL
2497 * certificate errors.
2498 */
2499 public void clearSslPreferences() {
2500 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2501 }
2502
2503 /**
2504 * Return the WebBackForwardList for this WebView. This contains the
2505 * back/forward list for use in querying each item in the history stack.
2506 * This is a copy of the private WebBackForwardList so it contains only a
2507 * snapshot of the current state. Multiple calls to this method may return
2508 * different objects. The object returned from this method will not be
2509 * updated to reflect any new state.
2510 */
2511 public WebBackForwardList copyBackForwardList() {
2512 return mCallbackProxy.getBackForwardList().clone();
2513 }
2514
2515 /*
2516 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04002517 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002518 * calling findAll.
2519 *
2520 * @param forward Direction to search.
2521 */
2522 public void findNext(boolean forward) {
Cary Clark7f970112009-10-15 15:29:08 -04002523 if (0 == mNativeClass) return; // client isn't initialized
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002524 nativeFindNext(forward);
2525 }
2526
2527 /*
2528 * Find all instances of find on the page and highlight them.
2529 * @param find String to find.
2530 * @return int The number of occurances of the String "find"
2531 * that were found.
2532 */
2533 public int findAll(String find) {
Cary Clark7f970112009-10-15 15:29:08 -04002534 if (0 == mNativeClass) return 0; // client isn't initialized
Cary Clarkde023c12010-03-03 10:05:16 -05002535 int result = find != null ? nativeFindAll(find.toLowerCase(),
2536 find.toUpperCase()) : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002537 invalidate();
Leon Scroggins5de63892009-10-29 09:48:43 -04002538 mLastFind = find;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002539 return result;
2540 }
2541
Cary Clark3403eb32010-03-03 10:05:16 -05002542 /**
Cary Clarkde023c12010-03-03 10:05:16 -05002543 * @hide
2544 */
2545 public void setFindIsUp(boolean isUp) {
2546 mFindIsUp = isUp;
2547 if (isUp) {
2548 recordNewContentSize(mContentWidth, mContentHeight + mFindHeight,
2549 false);
2550 }
2551 if (0 == mNativeClass) return; // client isn't initialized
2552 nativeSetFindIsUp(isUp);
2553 }
2554
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002555 // Used to know whether the find dialog is open. Affects whether
2556 // or not we draw the highlights for matches.
2557 private boolean mFindIsUp;
Cary Clarkde023c12010-03-03 10:05:16 -05002558
Cary Clark5bb6b522009-09-21 11:58:31 -04002559 private int mFindHeight;
Leon Scroggins5de63892009-10-29 09:48:43 -04002560 // Keep track of the last string sent, so we can search again after an
2561 // orientation change or the dismissal of the soft keyboard.
2562 private String mLastFind;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002563
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002564 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002565 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002566 * location. Currently, only addresses in the United States are detected,
2567 * and consist of:
2568 * - a house number
2569 * - a street name
2570 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2571 * - a city name
2572 * - a state or territory, either spelled out or two-letter abbr.
2573 * - an optional 5 digit or 9 digit zip code.
2574 *
2575 * All names must be correctly capitalized, and the zip code, if present,
2576 * must be valid for the state. The street type must be a standard USPS
2577 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04002578 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002579 * five digits.
2580 * @param addr The string to search for addresses.
2581 *
2582 * @return the address, or if no address is found, return null.
2583 */
2584 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04002585 return findAddress(addr, false);
2586 }
2587
2588 /**
2589 * @hide
2590 * Return the first substring consisting of the address of a physical
2591 * location. Currently, only addresses in the United States are detected,
2592 * and consist of:
2593 * - a house number
2594 * - a street name
2595 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2596 * - a city name
2597 * - a state or territory, either spelled out or two-letter abbr.
2598 * - an optional 5 digit or 9 digit zip code.
2599 *
2600 * Names are optionally capitalized, and the zip code, if present,
2601 * must be valid for the state. The street type must be a standard USPS
2602 * spelling or abbreviation. The state or territory must also be spelled
2603 * or abbreviated using USPS standards. The house number may not exceed
2604 * five digits.
2605 * @param addr The string to search for addresses.
2606 * @param caseInsensitive addr Set to true to make search ignore case.
2607 *
2608 * @return the address, or if no address is found, return null.
2609 */
2610 public static String findAddress(String addr, boolean caseInsensitive) {
2611 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002612 }
2613
2614 /*
2615 * Clear the highlighting surrounding text matches created by findAll.
2616 */
2617 public void clearMatches() {
Cary Clarkde023c12010-03-03 10:05:16 -05002618 mLastFind = "";
Cary Clark32847a92009-11-17 16:04:18 -05002619 if (mNativeClass == 0)
2620 return;
Cary Clarkde023c12010-03-03 10:05:16 -05002621 nativeSetFindIsEmpty();
2622 invalidate();
2623 }
2624
2625 /**
2626 * @hide
2627 */
2628 public void notifyFindDialogDismissed() {
2629 clearMatches();
2630 setFindIsUp(false);
2631 recordNewContentSize(mContentWidth, mContentHeight - mFindHeight,
2632 false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002633 // Now that the dialog has been removed, ensure that we scroll to a
2634 // location that is not beyond the end of the page.
2635 pinScrollTo(mScrollX, mScrollY, false, 0);
2636 invalidate();
2637 }
2638
2639 /**
Cary Clark5bb6b522009-09-21 11:58:31 -04002640 * @hide
2641 */
2642 public void setFindDialogHeight(int height) {
2643 if (DebugFlags.WEB_VIEW) {
2644 Log.v(LOGTAG, "setFindDialogHeight height=" + height);
2645 }
2646 mFindHeight = height;
2647 }
2648
2649 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002650 * Query the document to see if it contains any image references. The
2651 * message object will be dispatched with arg1 being set to 1 if images
2652 * were found and 0 if the document does not reference any images.
2653 * @param response The message that will be dispatched with the result.
2654 */
2655 public void documentHasImages(Message response) {
2656 if (response == null) {
2657 return;
2658 }
2659 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
2660 }
2661
2662 @Override
2663 public void computeScroll() {
2664 if (mScroller.computeScrollOffset()) {
2665 int oldX = mScrollX;
2666 int oldY = mScrollY;
Grace Klobad7625dd2010-03-04 11:46:12 -08002667 int x = mScroller.getCurrX();
2668 int y = mScroller.getCurrY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002669 postInvalidate(); // So we draw again
Grace Klobad7625dd2010-03-04 11:46:12 -08002670 if (oldX != x || oldY != y) {
2671 overscrollBy(x - oldX, y - oldY, oldX, oldY,
2672 computeMaxScrollX(), computeMaxScrollY(),
2673 getViewWidth() / 3, getViewHeight() / 3);
2674 onScrollChanged(mScrollX, mScrollY, oldX, oldY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002675 }
2676 } else {
2677 super.computeScroll();
2678 }
2679 }
2680
2681 private static int computeDuration(int dx, int dy) {
2682 int distance = Math.max(Math.abs(dx), Math.abs(dy));
2683 int duration = distance * 1000 / STD_SPEED;
2684 return Math.min(duration, MAX_DURATION);
2685 }
2686
2687 // helper to pin the scrollBy parameters (already in view coordinates)
2688 // returns true if the scroll was changed
2689 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
2690 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
2691 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002692 // helper to pin the scrollTo parameters (already in view coordinates)
2693 // returns true if the scroll was changed
2694 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
2695 x = pinLocX(x);
2696 y = pinLocY(y);
2697 int dx = x - mScrollX;
2698 int dy = y - mScrollY;
2699
2700 if ((dx | dy) == 0) {
2701 return false;
2702 }
Leon Scrogginsd55de402009-09-17 14:19:49 -04002703 if (animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002704 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002705 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
2706 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
Mike Cleronf116bf82009-09-27 19:14:12 -07002707 awakenScrollBars(mScroller.getDuration());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002708 invalidate();
2709 } else {
Cary Clark278ce052009-08-31 16:08:42 -04002710 abortAnimation(); // just in case
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002711 scrollTo(x, y);
2712 }
2713 return true;
2714 }
2715
2716 // Scale from content to view coordinates, and pin.
2717 // Also called by jni webview.cpp
Leon Scroggins4c943042009-07-31 15:05:42 -04002718 private boolean setContentScrollBy(int cx, int cy, boolean animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002719 if (mDrawHistory) {
2720 // disallow WebView to change the scroll position as History Picture
2721 // is used in the view system.
2722 // TODO: as we switchOutDrawHistory when trackball or navigation
2723 // keys are hit, this should be safe. Right?
Leon Scroggins4c943042009-07-31 15:05:42 -04002724 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002725 }
Mike Reede8853fc2009-09-04 14:01:48 -04002726 cx = contentToViewDimension(cx);
2727 cy = contentToViewDimension(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002728 if (mHeightCanMeasure) {
2729 // move our visible rect according to scroll request
2730 if (cy != 0) {
2731 Rect tempRect = new Rect();
2732 calcOurVisibleRect(tempRect);
2733 tempRect.offset(cx, cy);
2734 requestRectangleOnScreen(tempRect);
2735 }
2736 // FIXME: We scroll horizontally no matter what because currently
2737 // ScrollView and ListView will not scroll horizontally.
2738 // FIXME: Why do we only scroll horizontally if there is no
2739 // vertical scroll?
2740// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
Leon Scroggins4c943042009-07-31 15:05:42 -04002741 return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002742 } else {
Leon Scroggins4c943042009-07-31 15:05:42 -04002743 return pinScrollBy(cx, cy, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002744 }
2745 }
2746
Leon Scroggins405d7852009-11-02 14:50:54 -08002747 /**
2748 * Called by CallbackProxy when the page finishes loading.
2749 * @param url The URL of the page which has finished loading.
2750 */
2751 /* package */ void onPageFinished(String url) {
2752 if (mPageThatNeedsToSlideTitleBarOffScreen != null) {
2753 // If the user is now on a different page, or has scrolled the page
2754 // past the point where the title bar is offscreen, ignore the
2755 // scroll request.
2756 if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url)
2757 && mScrollX == 0 && mScrollY == 0) {
2758 pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true,
2759 SLIDE_TITLE_DURATION);
2760 }
2761 mPageThatNeedsToSlideTitleBarOffScreen = null;
2762 }
2763 }
2764
2765 /**
2766 * The URL of a page that sent a message to scroll the title bar off screen.
2767 *
2768 * Many mobile sites tell the page to scroll to (0,1) in order to scroll the
2769 * title bar off the screen. Sometimes, the scroll position is set before
2770 * the page finishes loading. Rather than scrolling while the page is still
2771 * loading, keep track of the URL and new scroll position so we can perform
2772 * the scroll once the page finishes loading.
2773 */
2774 private String mPageThatNeedsToSlideTitleBarOffScreen;
2775
2776 /**
2777 * The destination Y scroll position to be used when the page finishes
2778 * loading. See mPageThatNeedsToSlideTitleBarOffScreen.
2779 */
2780 private int mYDistanceToSlideTitleOffScreen;
2781
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002782 // scale from content to view coordinates, and pin
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002783 // return true if pin caused the final x/y different than the request cx/cy,
2784 // and a future scroll may reach the request cx/cy after our size has
2785 // changed
2786 // return false if the view scroll to the exact position as it is requested,
2787 // where negative numbers are taken to mean 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002788 private boolean setContentScrollTo(int cx, int cy) {
2789 if (mDrawHistory) {
2790 // disallow WebView to change the scroll position as History Picture
2791 // is used in the view system.
2792 // One known case where this is called is that WebCore tries to
2793 // restore the scroll position. As history Picture already uses the
2794 // saved scroll position, it is ok to skip this.
2795 return false;
2796 }
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002797 int vx;
2798 int vy;
2799 if ((cx | cy) == 0) {
2800 // If the page is being scrolled to (0,0), do not add in the title
2801 // bar's height, and simply scroll to (0,0). (The only other work
2802 // in contentToView_ is to multiply, so this would not change 0.)
2803 vx = 0;
2804 vy = 0;
2805 } else {
2806 vx = contentToViewX(cx);
2807 vy = contentToViewY(cy);
2808 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002809// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
2810// vx + " " + vy + "]");
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002811 // Some mobile sites attempt to scroll the title bar off the page by
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04002812 // scrolling to (0,1). If we are at the top left corner of the
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002813 // page, assume this is an attempt to scroll off the title bar, and
2814 // animate the title bar off screen slowly enough that the user can see
2815 // it.
Leon Scroggins405d7852009-11-02 14:50:54 -08002816 if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0
2817 && mTitleBar != null) {
2818 // FIXME: 100 should be defined somewhere as our max progress.
2819 if (getProgress() < 100) {
2820 // Wait to scroll the title bar off screen until the page has
2821 // finished loading. Keep track of the URL and the destination
2822 // Y position
2823 mPageThatNeedsToSlideTitleBarOffScreen = getUrl();
2824 mYDistanceToSlideTitleOffScreen = vy;
2825 } else {
2826 pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
2827 }
Leon Scroggins03c87bf2009-09-18 15:05:59 -04002828 // Since we are animating, we have not yet reached the desired
2829 // scroll position. Do not return true to request another attempt
2830 return false;
2831 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002832 pinScrollTo(vx, vy, false, 0);
Leon Scroggins83d4ba82009-09-17 17:27:13 -04002833 // If the request was to scroll to a negative coordinate, treat it as if
2834 // it was a request to scroll to 0
2835 if ((mScrollX != vx && cx >= 0) || (mScrollY != vy && cy >= 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002836 return true;
2837 } else {
2838 return false;
2839 }
2840 }
2841
2842 // scale from content to view coordinates, and pin
2843 private void spawnContentScrollTo(int cx, int cy) {
2844 if (mDrawHistory) {
2845 // disallow WebView to change the scroll position as History Picture
2846 // is used in the view system.
2847 return;
2848 }
Leon Scroggins0236e672009-09-02 21:12:08 -04002849 int vx = contentToViewX(cx);
2850 int vy = contentToViewY(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002851 pinScrollTo(vx, vy, true, 0);
2852 }
2853
2854 /**
2855 * These are from webkit, and are in content coordinate system (unzoomed)
2856 */
2857 private void contentSizeChanged(boolean updateLayout) {
2858 // suppress 0,0 since we usually see real dimensions soon after
2859 // this avoids drawing the prev content in a funny place. If we find a
2860 // way to consolidate these notifications, this check may become
2861 // obsolete
2862 if ((mContentWidth | mContentHeight) == 0) {
2863 return;
2864 }
2865
2866 if (mHeightCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002867 if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002868 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002869 requestLayout();
2870 }
2871 } else if (mWidthCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04002872 if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
Grace Kloba3f9faf42009-10-13 14:13:54 -07002873 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002874 requestLayout();
2875 }
2876 } else {
2877 // If we don't request a layout, try to send our view size to the
2878 // native side to ensure that WebCore has the correct dimensions.
2879 sendViewSizeZoom();
2880 }
2881 }
2882
2883 /**
2884 * Set the WebViewClient that will receive various notifications and
2885 * requests. This will replace the current handler.
2886 * @param client An implementation of WebViewClient.
2887 */
2888 public void setWebViewClient(WebViewClient client) {
2889 mCallbackProxy.setWebViewClient(client);
2890 }
2891
2892 /**
Grace Kloba94ab3b62009-10-07 18:00:19 -07002893 * Gets the WebViewClient
2894 * @return the current WebViewClient instance.
2895 *
2896 *@hide pending API council approval.
2897 */
2898 public WebViewClient getWebViewClient() {
2899 return mCallbackProxy.getWebViewClient();
2900 }
2901
2902 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002903 * Register the interface to be used when content can not be handled by
2904 * the rendering engine, and should be downloaded instead. This will replace
2905 * the current handler.
2906 * @param listener An implementation of DownloadListener.
2907 */
2908 public void setDownloadListener(DownloadListener listener) {
2909 mCallbackProxy.setDownloadListener(listener);
2910 }
2911
2912 /**
2913 * Set the chrome handler. This is an implementation of WebChromeClient for
2914 * use in handling Javascript dialogs, favicons, titles, and the progress.
2915 * This will replace the current handler.
2916 * @param client An implementation of WebChromeClient.
2917 */
2918 public void setWebChromeClient(WebChromeClient client) {
2919 mCallbackProxy.setWebChromeClient(client);
2920 }
2921
2922 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01002923 * Gets the chrome handler.
2924 * @return the current WebChromeClient instance.
2925 *
2926 * @hide API council approval.
2927 */
2928 public WebChromeClient getWebChromeClient() {
2929 return mCallbackProxy.getWebChromeClient();
2930 }
2931
2932 /**
Patrick Scott0b2e84b2010-03-02 08:58:44 -05002933 * Set the back/forward list client. This is an implementation of
2934 * WebBackForwardListClient for handling new items and changes in the
2935 * history index.
2936 * @param client An implementation of WebBackForwardListClient.
2937 * {@hide}
2938 */
2939 public void setWebBackForwardListClient(WebBackForwardListClient client) {
2940 mCallbackProxy.setWebBackForwardListClient(client);
2941 }
2942
2943 /**
2944 * Gets the WebBackForwardListClient.
2945 * {@hide}
2946 */
2947 public WebBackForwardListClient getWebBackForwardListClient() {
2948 return mCallbackProxy.getWebBackForwardListClient();
2949 }
2950
2951 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002952 * Set the Picture listener. This is an interface used to receive
2953 * notifications of a new Picture.
2954 * @param listener An implementation of WebView.PictureListener.
2955 */
2956 public void setPictureListener(PictureListener listener) {
2957 mPictureListener = listener;
2958 }
2959
2960 /**
2961 * {@hide}
2962 */
2963 /* FIXME: Debug only! Remove for SDK! */
2964 public void externalRepresentation(Message callback) {
2965 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
2966 }
2967
2968 /**
2969 * {@hide}
2970 */
2971 /* FIXME: Debug only! Remove for SDK! */
2972 public void documentAsText(Message callback) {
2973 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
2974 }
2975
2976 /**
2977 * Use this function to bind an object to Javascript so that the
2978 * methods can be accessed from Javascript.
2979 * <p><strong>IMPORTANT:</strong>
2980 * <ul>
2981 * <li> Using addJavascriptInterface() allows JavaScript to control your
2982 * application. This can be a very useful feature or a dangerous security
2983 * issue. When the HTML in the WebView is untrustworthy (for example, part
2984 * or all of the HTML is provided by some person or process), then an
2985 * attacker could inject HTML that will execute your code and possibly any
2986 * code of the attacker's choosing.<br>
2987 * Do not use addJavascriptInterface() unless all of the HTML in this
2988 * WebView was written by you.</li>
2989 * <li> The Java object that is bound runs in another thread and not in
2990 * the thread that it was constructed in.</li>
2991 * </ul></p>
2992 * @param obj The class instance to bind to Javascript
2993 * @param interfaceName The name to used to expose the class in Javascript
2994 */
2995 public void addJavascriptInterface(Object obj, String interfaceName) {
Cary Clarkded054c2009-06-15 10:26:08 -04002996 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
2997 arg.mObject = obj;
2998 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002999 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
3000 }
3001
3002 /**
3003 * Return the WebSettings object used to control the settings for this
3004 * WebView.
3005 * @return A WebSettings object that can be used to control this WebView's
3006 * settings.
3007 */
3008 public WebSettings getSettings() {
3009 return mWebViewCore.getSettings();
3010 }
3011
Andrei Popescua6d747d2010-02-11 13:19:21 +00003012 /**
3013 * Use this method to inform the webview about packages that are installed
3014 * in the system. This information will be used by the
3015 * navigator.isApplicationInstalled() API.
3016 * @param packageNames is a set of package names that are known to be
3017 * installed in the system.
3018 *
3019 * @hide not a public API
3020 */
3021 public void addPackageNames(Set<String> packageNames) {
3022 mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, packageNames);
3023 }
3024
3025 /**
3026 * Use this method to inform the webview about single packages that are
3027 * installed in the system. This information will be used by the
3028 * navigator.isApplicationInstalled() API.
3029 * @param packageName is the name of a package that is known to be
3030 * installed in the system.
3031 *
3032 * @hide not a public API
3033 */
3034 public void addPackageName(String packageName) {
3035 mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAME, packageName);
3036 }
3037
3038 /**
3039 * Use this method to inform the webview about packages that are uninstalled
3040 * in the system. This information will be used by the
3041 * navigator.isApplicationInstalled() API.
3042 * @param packageName is the name of a package that has been uninstalled in
3043 * the system.
3044 *
3045 * @hide not a public API
3046 */
3047 public void removePackageName(String packageName) {
3048 mWebViewCore.sendMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
3049 }
3050
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003051 /**
3052 * Return the list of currently loaded plugins.
3053 * @return The list of currently loaded plugins.
Andrei Popescu385df692009-08-13 11:59:57 +01003054 *
3055 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003056 */
Andrei Popescu385df692009-08-13 11:59:57 +01003057 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003058 public static synchronized PluginList getPluginList() {
Grace Klobabb245ea2009-11-10 13:13:24 -08003059 return new PluginList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003060 }
3061
3062 /**
Andrei Popescu385df692009-08-13 11:59:57 +01003063 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003064 */
Andrei Popescu385df692009-08-13 11:59:57 +01003065 @Deprecated
3066 public void refreshPlugins(boolean reloadOpenPages) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003067
3068 //-------------------------------------------------------------------------
3069 // Override View methods
3070 //-------------------------------------------------------------------------
3071
3072 @Override
3073 protected void finalize() throws Throwable {
Cary Clark9a4c0632009-08-10 16:40:08 -04003074 try {
3075 destroy();
3076 } finally {
3077 super.finalize();
3078 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003079 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003081 @Override
Leon Scroggins0236e672009-09-02 21:12:08 -04003082 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
3083 if (child == mTitleBar) {
3084 // When drawing the title bar, move it horizontally to always show
Grace Klobad7625dd2010-03-04 11:46:12 -08003085 // at the top of the WebView. While overscroll, stick the title bar
3086 // on the top otherwise we may have two during loading, one is drawn
3087 // here, another is drawn by the Browser.
Leon Scroggins0236e672009-09-02 21:12:08 -04003088 mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
Grace Klobad7625dd2010-03-04 11:46:12 -08003089 if (mScrollY <= 0) {
3090 mTitleBar.offsetTopAndBottom(mScrollY - mTitleBar.getTop());
3091 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003092 }
3093 return super.drawChild(canvas, child, drawingTime);
3094 }
3095
Mike Reed19f3f0e2009-11-12 12:50:20 -05003096 private void drawContent(Canvas canvas) {
Grace Kloba04b28682009-09-14 14:38:37 -07003097 // Update the buttons in the picture, so when we draw the picture
3098 // to the screen, they are in the correct state.
3099 // Tell the native side if user is a) touching the screen,
3100 // b) pressing the trackball down, or c) pressing the enter key
3101 // If the cursor is on a button, we need to draw it in the pressed
3102 // state.
3103 // If mNativeClass is 0, we should not reach here, so we do not
3104 // need to check it again.
3105 nativeRecordButtons(hasFocus() && hasWindowFocus(),
Mike Reed19f3f0e2009-11-12 12:50:20 -05003106 mTouchMode == TOUCH_SHORTPRESS_START_MODE
3107 || mTrackballDown || mGotCenterDown, false);
Grace Kloba04b28682009-09-14 14:38:37 -07003108 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
Mike Reed19f3f0e2009-11-12 12:50:20 -05003109 }
3110
3111 @Override
3112 protected void onDraw(Canvas canvas) {
3113 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
3114 if (mNativeClass == 0) {
3115 return;
3116 }
3117
3118 int saveCount = canvas.save();
Grace Kloba2d17e1d2010-03-09 10:55:04 -08003119 if (mInOverScrollMode
3120 && getSettings().getUseSystemOverscrollBackground()) {
Grace Klobad7625dd2010-03-04 11:46:12 -08003121 if (mOverScrollBackground == null) {
3122 mOverScrollBackground = new Paint();
3123 Bitmap bm = BitmapFactory.decodeResource(
3124 mContext.getResources(),
3125 com.android.internal.R.drawable.pattern_underwear);
3126 mOverScrollBackground.setShader(new BitmapShader(bm,
3127 Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
3128 }
3129 int top = getTitleHeight();
3130 // first draw the background and anchor to the top of the view
3131 canvas.save();
3132 canvas.translate(mScrollX, mScrollY);
3133 canvas.clipRect(-mScrollX, top - mScrollY,
3134 computeHorizontalScrollRange() - mScrollX, top
3135 + computeVerticalScrollRange() - mScrollY,
3136 Region.Op.DIFFERENCE);
3137 canvas.drawPaint(mOverScrollBackground);
3138 canvas.restore();
3139 // next clip the region for the content
3140 canvas.clipRect(0, top, computeHorizontalScrollRange(), top
3141 + computeVerticalScrollRange());
3142 }
Mike Reed19f3f0e2009-11-12 12:50:20 -05003143 if (mTitleBar != null) {
3144 canvas.translate(0, (int) mTitleBar.getHeight());
3145 }
Mike Reedc95326092010-02-08 16:49:58 -05003146 if (mDragTrackerHandler == null) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05003147 drawContent(canvas);
Mike Reedc95326092010-02-08 16:49:58 -05003148 } else {
3149 if (!mDragTrackerHandler.draw(canvas)) {
3150 // sometimes the tracker doesn't draw, even though its active
3151 drawContent(canvas);
3152 }
3153 if (mDragTrackerHandler.isFinished()) {
3154 mDragTrackerHandler = null;
3155 }
Mike Reed19f3f0e2009-11-12 12:50:20 -05003156 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003157 canvas.restoreToCount(saveCount);
Cary Clarkd6982c92009-05-29 11:02:22 -04003158
Leon Scroggins58992ea2009-09-17 15:55:31 -04003159 // Now draw the shadow.
Grace Klobad7625dd2010-03-04 11:46:12 -08003160 int titleH = getVisibleTitleHeight();
3161 if (mTitleBar != null && titleH == 0) {
Leon Scroggins58992ea2009-09-17 15:55:31 -04003162 int height = (int) (5f * getContext().getResources()
3163 .getDisplayMetrics().density);
Grace Klobad7625dd2010-03-04 11:46:12 -08003164 mTitleShadow.setBounds(mScrollX, mScrollY, mScrollX + getWidth(),
3165 mScrollY + height);
Leon Scroggins58992ea2009-09-17 15:55:31 -04003166 mTitleShadow.draw(canvas);
3167 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003168 if (AUTO_REDRAW_HACK && mAutoRedraw) {
3169 invalidate();
3170 }
Nicolas Roard38863332010-01-04 19:30:55 +00003171 mWebViewCore.signalRepaintDone();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003172 }
3173
3174 @Override
3175 public void setLayoutParams(ViewGroup.LayoutParams params) {
3176 if (params.height == LayoutParams.WRAP_CONTENT) {
3177 mWrapContent = true;
3178 }
3179 super.setLayoutParams(params);
3180 }
3181
3182 @Override
3183 public boolean performLongClick() {
Grace Kloba98e6fcf2010-01-27 15:20:30 -08003184 // performLongClick() is the result of a delayed message. If we switch
3185 // to windows overview, the WebView will be temporarily removed from the
3186 // view system. In that case, do nothing.
3187 if (getParent() == null) return false;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003188 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
3189 // Send the click so that the textfield is in focus
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003190 centerKeyPressOnTextField();
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003191 rebuildWebTextView();
3192 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003193 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003194 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003195 } else {
3196 return super.performLongClick();
3197 }
3198 }
3199
Grace Kloba94c715d2009-09-28 23:23:53 -07003200 boolean inAnimateZoom() {
3201 return mZoomScale != 0;
3202 }
3203
Leon Scroggins608f9f42009-09-03 10:06:04 -04003204 /**
3205 * Need to adjust the WebTextView after a change in zoom, since mActualScale
3206 * has changed. This is especially important for password fields, which are
3207 * drawn by the WebTextView, since it conveys more information than what
3208 * webkit draws. Thus we need to reposition it to show in the correct
3209 * place.
3210 */
3211 private boolean mNeedToAdjustWebTextView;
3212
Cary Clark5da9aeb2009-10-06 17:40:53 -04003213 private boolean didUpdateTextViewBounds(boolean allowIntersect) {
3214 Rect contentBounds = nativeFocusCandidateNodeBounds();
3215 Rect vBox = contentToViewRect(contentBounds);
3216 Rect visibleRect = new Rect();
3217 calcOurVisibleRect(visibleRect);
Leon Scrogginsbcbf5642010-02-17 17:56:51 -05003218 // If the textfield is on screen, place the WebTextView in
3219 // its new place, accounting for our new scroll/zoom values,
3220 // and adjust its textsize.
3221 if (allowIntersect ? Rect.intersects(visibleRect, vBox)
3222 : visibleRect.contains(vBox)) {
Cary Clark5da9aeb2009-10-06 17:40:53 -04003223 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
3224 vBox.height());
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003225 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
3226 contentToViewDimension(
3227 nativeFocusCandidateTextSize()));
Cary Clark5da9aeb2009-10-06 17:40:53 -04003228 return true;
3229 } else {
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003230 // The textfield is now off screen. The user probably
3231 // was not zooming to see the textfield better. Remove
3232 // the WebTextView. If the user types a key, and the
3233 // textfield is still in focus, we will reconstruct
3234 // the WebTextView and scroll it back on screen.
3235 mWebTextView.remove();
Cary Clark5da9aeb2009-10-06 17:40:53 -04003236 return false;
3237 }
3238 }
3239
Cary Clark2ec30692010-02-23 10:50:38 -05003240 private void drawExtras(Canvas canvas, int extras) {
Cary Clark018ff892010-02-25 14:49:26 -05003241 // If mNativeClass is 0, we should not reach here, so we do not
3242 // need to check it again.
Cary Clark2ec30692010-02-23 10:50:38 -05003243 // Currently for each draw we compute the animation values;
3244 // We may in the future decide to do that independently.
3245 if (nativeEvaluateLayersAnimations()) {
3246 // If we have unfinished (or unstarted) animations,
3247 // we ask for a repaint.
3248 invalidate();
Nicolas Roard38863332010-01-04 19:30:55 +00003249 }
Cary Clark2ec30692010-02-23 10:50:38 -05003250
3251 nativeDrawExtras(canvas, extras);
Nicolas Roard38863332010-01-04 19:30:55 +00003252 }
3253
Cary Clarkd6982c92009-05-29 11:02:22 -04003254 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003255 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003256 if (mDrawHistory) {
3257 canvas.scale(mActualScale, mActualScale);
3258 canvas.drawPicture(mHistoryPicture);
3259 return;
3260 }
3261
3262 boolean animateZoom = mZoomScale != 0;
Grace Klobac2242f22010-03-05 14:00:26 -08003263 boolean animateScroll = ((!mScroller.isFinished()
Cary Clark25415e22009-10-12 13:41:28 -04003264 || mVelocityTracker != null)
3265 && (mTouchMode != TOUCH_DRAG_MODE ||
Grace Klobac2242f22010-03-05 14:00:26 -08003266 mHeldMotionless != MOTIONLESS_TRUE))
3267 || mDeferTouchMode == TOUCH_DRAG_MODE;
Cary Clark25415e22009-10-12 13:41:28 -04003268 if (mTouchMode == TOUCH_DRAG_MODE) {
3269 if (mHeldMotionless == MOTIONLESS_PENDING) {
3270 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
3271 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
3272 mHeldMotionless = MOTIONLESS_FALSE;
3273 }
3274 if (mHeldMotionless == MOTIONLESS_FALSE) {
3275 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3276 .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
3277 mHeldMotionless = MOTIONLESS_PENDING;
3278 }
3279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003280 if (animateZoom) {
3281 float zoomScale;
3282 int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
3283 if (interval < ZOOM_ANIMATION_LENGTH) {
3284 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
Cary Clarkd6982c92009-05-29 11:02:22 -04003285 zoomScale = 1.0f / (mInvInitialZoomScale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003286 + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
3287 invalidate();
3288 } else {
3289 zoomScale = mZoomScale;
3290 // set mZoomScale to be 0 as we have done animation
3291 mZoomScale = 0;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08003292 WebViewCore.resumeUpdatePicture(mWebViewCore);
Grace Klobadfe095a2009-09-17 07:56:48 -07003293 // call invalidate() again to draw with the final filters
3294 invalidate();
Leon Scroggins608f9f42009-09-03 10:06:04 -04003295 if (mNeedToAdjustWebTextView) {
3296 mNeedToAdjustWebTextView = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003297 if (didUpdateTextViewBounds(false)
3298 && nativeFocusCandidateIsPassword()) {
Leon Scroggins10be7542009-09-30 18:01:38 -04003299 // If it is a password field, start drawing the
3300 // WebTextView once again.
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003301 mWebTextView.setInPassword(true);
Leon Scroggins608f9f42009-09-03 10:06:04 -04003302 }
3303 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003304 }
Grace Klobac0c03af2009-09-17 11:01:44 -07003305 // calculate the intermediate scroll position. As we need to use
3306 // zoomScale, we can't use pinLocX/Y directly. Copy the logic here.
Grace Kloba675c7d22009-07-23 09:21:21 -07003307 float scale = zoomScale * mInvInitialZoomScale;
3308 int tx = Math.round(scale * (mInitialScrollX + mZoomCenterX)
3309 - mZoomCenterX);
3310 tx = -pinLoc(tx, getViewWidth(), Math.round(mContentWidth
3311 * zoomScale)) + mScrollX;
Grace Klobac0c03af2009-09-17 11:01:44 -07003312 int titleHeight = getTitleHeight();
3313 int ty = Math.round(scale
3314 * (mInitialScrollY + mZoomCenterY - titleHeight)
3315 - (mZoomCenterY - titleHeight));
3316 ty = -(ty <= titleHeight ? Math.max(ty, 0) : pinLoc(ty
3317 - titleHeight, getViewHeight(), Math.round(mContentHeight
3318 * zoomScale)) + titleHeight) + mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003319 canvas.translate(tx, ty);
3320 canvas.scale(zoomScale, zoomScale);
Leon Scroggins608f9f42009-09-03 10:06:04 -04003321 if (inEditingMode() && !mNeedToAdjustWebTextView
3322 && mZoomScale != 0) {
3323 // The WebTextView is up. Keep track of this so we can adjust
3324 // its size and placement when we finish zooming
3325 mNeedToAdjustWebTextView = true;
3326 // If it is in password mode, turn it off so it does not draw
3327 // misplaced.
3328 if (nativeFocusCandidateIsPassword()) {
3329 mWebTextView.setInPassword(false);
3330 }
3331 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003332 } else {
3333 canvas.scale(mActualScale, mActualScale);
3334 }
3335
Grace Kloba3a0def22010-01-23 21:11:54 -08003336 mWebViewCore.drawContentPicture(canvas, color,
3337 (animateZoom || mPreviewZoomOnly), animateScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003338 if (mNativeClass == 0) return;
Cary Clark2ec30692010-02-23 10:50:38 -05003339 // decide which adornments to draw
3340 int extras = DRAW_EXTRAS_NONE;
3341 if (mFindIsUp) {
3342 // When the FindDialog is up, only draw the matches if we are not in
3343 // the process of scrolling them into view.
3344 if (!animateScroll) {
3345 extras = DRAW_EXTRAS_FIND;
Cary Clark09e383c2009-10-26 16:43:58 -04003346 }
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003347 } else if (mShiftIsPressed && !nativeFocusIsPlugin()) {
Cary Clark2ec30692010-02-23 10:50:38 -05003348 if (!animateZoom && !mPreviewZoomOnly) {
3349 extras = DRAW_EXTRAS_SELECTION;
3350 nativeSetSelectionRegion(mTouchSelection || mExtendSelection);
3351 nativeSetSelectionPointer(!mTouchSelection, mInvActualScale,
3352 mSelectX, mSelectY - getTitleHeight(),
3353 mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003354 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003355 } else if (drawCursorRing) {
Cary Clark2ec30692010-02-23 10:50:38 -05003356 extras = DRAW_EXTRAS_CURSOR_RING;
3357 }
3358 drawExtras(canvas, extras);
3359
3360 if (extras == DRAW_EXTRAS_CURSOR_RING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003361 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3362 mTouchMode = TOUCH_SHORTPRESS_MODE;
3363 HitTestResult hitTest = getHitTestResult();
Grace Klobac2242f22010-03-05 14:00:26 -08003364 if (hitTest == null
3365 || hitTest.mType == HitTestResult.UNKNOWN_TYPE) {
3366 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003367 }
3368 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003369 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04003370 if (mFocusSizeChanged) {
3371 mFocusSizeChanged = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05003372 // If we are zooming, this will get handled above, when the zoom
3373 // finishes. We also do not need to do this unless the WebTextView
3374 // is showing.
3375 if (!animateZoom && inEditingMode()) {
3376 didUpdateTextViewBounds(true);
3377 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04003378 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003379 }
3380
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003381 // draw history
3382 private boolean mDrawHistory = false;
3383 private Picture mHistoryPicture = null;
3384 private int mHistoryWidth = 0;
3385 private int mHistoryHeight = 0;
3386
3387 // Only check the flag, can be called from WebCore thread
3388 boolean drawHistory() {
3389 return mDrawHistory;
3390 }
3391
3392 // Should only be called in UI thread
3393 void switchOutDrawHistory() {
3394 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Cary Clarkbc2e33b2009-04-23 13:12:19 -04003395 if (mDrawHistory && mWebViewCore.pictureReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003396 mDrawHistory = false;
3397 invalidate();
3398 int oldScrollX = mScrollX;
3399 int oldScrollY = mScrollY;
3400 mScrollX = pinLocX(mScrollX);
3401 mScrollY = pinLocY(mScrollY);
3402 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
3403 mUserScroll = false;
3404 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
3405 oldScrollY);
3406 }
3407 sendOurVisibleRect();
3408 }
3409 }
3410
Cary Clarkd6982c92009-05-29 11:02:22 -04003411 WebViewCore.CursorData cursorData() {
3412 WebViewCore.CursorData result = new WebViewCore.CursorData();
3413 result.mMoveGeneration = nativeMoveGeneration();
3414 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04003415 Point position = nativeCursorPosition();
3416 result.mX = position.x;
3417 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04003418 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003419 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003420
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003421 /**
3422 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04003423 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003424 * order, swap them.
3425 * @param start Beginning of selection to delete.
3426 * @param end End of selection to delete.
3427 */
3428 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003429 mTextGeneration++;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04003430 WebViewCore.TextSelectionData data
3431 = new WebViewCore.TextSelectionData(start, end);
3432 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
3433 data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003434 }
3435
3436 /**
3437 * Set the selection to (start, end) in the focused textfield. If start and
3438 * end are out of order, swap them.
3439 * @param start Beginning of selection.
3440 * @param end End of selection.
3441 */
3442 /* package */ void setSelection(int start, int end) {
Cary Clark2f1d60c2009-06-03 08:05:53 -04003443 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003444 }
3445
Derek Sollenberger7cabb032010-01-21 10:37:38 -05003446 @Override
3447 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
3448 InputConnection connection = super.onCreateInputConnection(outAttrs);
3449 outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN;
3450 return connection;
3451 }
3452
Leon Scroggins04e0a102010-01-08 16:19:27 -05003453 /**
3454 * Called in response to a message from webkit telling us that the soft
3455 * keyboard should be launched.
3456 */
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003457 private void displaySoftKeyboard(boolean isTextView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003458 InputMethodManager imm = (InputMethodManager)
3459 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003460
Leon Scrogginse3844ee2010-02-23 15:15:16 -05003461 // bring it back to the default scale so that user can enter text
3462 boolean zoom = mActualScale < mDefaultScale;
3463 if (zoom) {
3464 mInZoomOverview = false;
3465 mZoomCenterX = mLastTouchX;
3466 mZoomCenterY = mLastTouchY;
3467 // do not change text wrap scale so that there is no reflow
3468 setNewZoomScale(mDefaultScale, false, false);
3469 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003470 if (isTextView) {
Leon Scroggins04e0a102010-01-08 16:19:27 -05003471 rebuildWebTextView();
Leon Scrogginse3844ee2010-02-23 15:15:16 -05003472 if (inEditingMode()) {
3473 imm.showSoftInput(mWebTextView, 0);
3474 if (zoom) {
3475 didUpdateTextViewBounds(true);
3476 }
3477 return;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07003478 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003479 }
Leon Scrogginse3844ee2010-02-23 15:15:16 -05003480 // Used by plugins.
3481 // Also used if the navigation cache is out of date, and
3482 // does not recognize that a textfield is in focus. In that
3483 // case, use WebView as the targeted view.
3484 // see http://b/issue?id=2457459
3485 imm.showSoftInput(this, 0);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003486 }
3487
3488 // Called by WebKit to instruct the UI to hide the keyboard
3489 private void hideSoftKeyboard() {
3490 InputMethodManager imm = (InputMethodManager)
3491 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
3492
3493 imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003494 }
3495
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003496 /*
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003497 * This method checks the current focus and cursor and potentially rebuilds
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003498 * mWebTextView to have the appropriate properties, such as password,
3499 * multiline, and what text it contains. It also removes it if necessary.
3500 */
Leon Scroggins01058282009-07-30 16:33:56 -04003501 /* package */ void rebuildWebTextView() {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003502 // If the WebView does not have focus, do nothing until it gains focus.
Grace Kloba04b28682009-09-14 14:38:37 -07003503 if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003504 return;
3505 }
3506 boolean alreadyThere = inEditingMode();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003507 // inEditingMode can only return true if mWebTextView is non-null,
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003508 // so we can safely call remove() if (alreadyThere)
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003509 if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003510 if (alreadyThere) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003511 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003512 }
3513 return;
3514 }
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003515 // At this point, we know we have found an input field, so go ahead
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003516 // and create the WebTextView if necessary.
3517 if (mWebTextView == null) {
3518 mWebTextView = new WebTextView(mContext, WebView.this);
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003519 // Initialize our generation number.
3520 mTextGeneration = 0;
3521 }
Leon Scroggins3a6c88c2009-09-09 14:50:23 -04003522 mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
3523 contentToViewDimension(nativeFocusCandidateTextSize()));
Cary Clark3524be92009-06-22 13:09:11 -04003524 Rect visibleRect = new Rect();
3525 calcOurContentVisibleRect(visibleRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003526 // Note that sendOurVisibleRect calls viewToContent, so the coordinates
3527 // should be in content coordinates.
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003528 Rect bounds = nativeFocusCandidateNodeBounds();
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003529 Rect vBox = contentToViewRect(bounds);
3530 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height());
Cary Clarkd6982c92009-05-29 11:02:22 -04003531 if (!Rect.intersects(bounds, visibleRect)) {
Leon Scroggins40981262009-07-01 10:57:47 -04003532 mWebTextView.bringIntoView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003533 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003534 String text = nativeFocusCandidateText();
3535 int nodePointer = nativeFocusCandidatePointer();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003536 if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003537 // It is possible that we have the same textfield, but it has moved,
3538 // i.e. In the case of opening/closing the screen.
3539 // In that case, we need to set the dimensions, but not the other
3540 // aspects.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003541 // If the text has been changed by webkit, update it. However, if
3542 // there has been more UI text input, ignore it. We will receive
3543 // another update when that text is recognized.
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003544 if (text != null && !text.equals(mWebTextView.getText().toString())
Cary Clarkd6982c92009-05-29 11:02:22 -04003545 && nativeTextGeneration() == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003546 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003547 }
3548 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003549 mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
3550 Gravity.RIGHT : Gravity.NO_GRAVITY);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003551 // This needs to be called before setType, which may call
3552 // requestFormData, and it needs to have the correct nodePointer.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003553 mWebTextView.setNodePointer(nodePointer);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003554 mWebTextView.setType(nativeFocusCandidateType());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003555 if (null == text) {
Cary Clark243ea062009-06-25 10:49:32 -04003556 if (DebugFlags.WEB_VIEW) {
3557 Log.v(LOGTAG, "rebuildWebTextView null == text");
3558 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003559 text = "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003560 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003561 mWebTextView.setTextAndKeepSelection(text);
Leon Scroggins5c84bf02010-02-09 17:03:44 -05003562 InputMethodManager imm = InputMethodManager.peekInstance();
3563 if (imm != null && imm.isActive(mWebTextView)) {
3564 imm.restartInput(mWebTextView);
3565 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003566 }
Leon Scroggins6be3bf22009-12-08 13:43:47 -05003567 mWebTextView.requestFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003568 }
3569
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05003570 /**
3571 * Called by WebTextView to find saved form data associated with the
3572 * textfield
3573 * @param name Name of the textfield.
3574 * @param nodePointer Pointer to the node of the textfield, so it can be
3575 * compared to the currently focused textfield when the data is
3576 * retrieved.
3577 */
3578 /* package */ void requestFormData(String name, int nodePointer) {
3579 if (mWebViewCore.getSettings().getSaveFormData()) {
3580 Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
3581 update.arg1 = nodePointer;
3582 RequestFormData updater = new RequestFormData(name, getUrl(),
3583 update);
3584 Thread t = new Thread(updater);
3585 t.start();
3586 }
3587 }
3588
Leon Scroggins3a503392010-01-06 17:04:38 -05003589 /**
3590 * Pass a message to find out the <label> associated with the <input>
3591 * identified by nodePointer
3592 * @param framePointer Pointer to the frame containing the <input> node
3593 * @param nodePointer Pointer to the node for which a <label> is desired.
3594 */
3595 /* package */ void requestLabel(int framePointer, int nodePointer) {
3596 mWebViewCore.sendMessage(EventHub.REQUEST_LABEL, framePointer,
3597 nodePointer);
3598 }
3599
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003600 /*
3601 * This class requests an Adapter for the WebTextView which shows past
3602 * entries stored in the database. It is a Runnable so that it can be done
3603 * in its own thread, without slowing down the UI.
3604 */
3605 private class RequestFormData implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003606 private String mName;
3607 private String mUrl;
3608 private Message mUpdateMessage;
3609
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003610 public RequestFormData(String name, String url, Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003611 mName = name;
3612 mUrl = url;
3613 mUpdateMessage = msg;
3614 }
3615
3616 public void run() {
3617 ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
3618 if (pastEntries.size() > 0) {
3619 AutoCompleteAdapter adapter = new
3620 AutoCompleteAdapter(mContext, pastEntries);
Cary Clarkded054c2009-06-15 10:26:08 -04003621 mUpdateMessage.obj = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003622 mUpdateMessage.sendToTarget();
3623 }
3624 }
3625 }
3626
Grace Kloba8ae1b412009-11-23 10:35:34 -08003627 /**
3628 * Dump the display tree to "/sdcard/displayTree.txt"
3629 *
3630 * @hide debug only
3631 */
3632 public void dumpDisplayTree() {
3633 nativeDumpDisplayTree(getUrl());
3634 }
3635
3636 /**
3637 * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
3638 * "/sdcard/domTree.txt"
3639 *
3640 * @hide debug only
3641 */
3642 public void dumpDomTree(boolean toFile) {
3643 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
3644 }
3645
3646 /**
3647 * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
3648 * to "/sdcard/renderTree.txt"
3649 *
3650 * @hide debug only
3651 */
3652 public void dumpRenderTree(boolean toFile) {
3653 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
3654 }
3655
Andrei Popescu5e7bb0a2010-02-01 22:32:16 +00003656 /**
3657 * Dump the V8 counters to standard output.
3658 * Note that you need a build with V8 and WEBCORE_INSTRUMENTATION set to
3659 * true. Otherwise, this will do nothing.
3660 *
3661 * @hide debug only
3662 */
3663 public void dumpV8Counters() {
3664 mWebViewCore.sendMessage(EventHub.DUMP_V8COUNTERS);
3665 }
3666
Leon Scrogginse3225672009-06-03 15:53:13 -04003667 // This is used to determine long press with the center key. Does not
3668 // affect long press with the trackball/touch.
3669 private boolean mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003670
3671 @Override
3672 public boolean onKeyDown(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003673 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003674 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003675 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003676 }
3677
3678 if (mNativeClass == 0) {
3679 return false;
3680 }
3681
3682 // do this hack up front, so it always works, regardless of touch-mode
3683 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
3684 mAutoRedraw = !mAutoRedraw;
3685 if (mAutoRedraw) {
3686 invalidate();
3687 }
3688 return true;
3689 }
3690
3691 // Bubble up the key event if
3692 // 1. it is a system key; or
Grace Kloba04b28682009-09-14 14:38:37 -07003693 // 2. the host application wants to handle it;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003694 if (event.isSystem()
Grace Kloba04b28682009-09-14 14:38:37 -07003695 || mCallbackProxy.uiOverrideKeyEvent(event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003696 return false;
3697 }
3698
Leon Scroggins7a375872010-03-09 14:26:07 -05003699 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
3700 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
3701 if (nativeFocusIsPlugin()) {
3702 mShiftIsPressed = true;
3703 } else if (!nativeCursorWantsKeyEvents() && !mShiftIsPressed) {
3704 setUpSelectXY();
3705 }
Cary Clark09e383c2009-10-26 16:43:58 -04003706 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003707
3708 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3709 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003710 switchOutDrawHistory();
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003711 if (nativeFocusIsPlugin()) {
3712 letPluginHandleNavKey(keyCode, event.getEventTime(), true);
3713 return true;
3714 }
Cary Clarkc05af372009-10-16 10:52:27 -04003715 if (mShiftIsPressed) {
3716 int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
3717 ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
3718 int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
3719 -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0;
3720 int multiplier = event.getRepeatCount() + 1;
3721 moveSelection(xRate * multiplier, yRate * multiplier);
3722 return true;
3723 }
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003724 if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003725 playSoundEffect(keyCodeToSoundsEffect(keyCode));
3726 return true;
3727 }
3728 // Bubble up the key event as WebView doesn't handle it
3729 return false;
3730 }
3731
Leon Scrogginse3225672009-06-03 15:53:13 -04003732 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003733 switchOutDrawHistory();
3734 if (event.getRepeatCount() == 0) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003735 if (mShiftIsPressed && !nativeFocusIsPlugin()) {
Cary Clarkc05af372009-10-16 10:52:27 -04003736 return true; // discard press if copy in progress
3737 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003738 mGotCenterDown = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003739 mPrivateHandler.sendMessageDelayed(mPrivateHandler
Leon Scrogginse3225672009-06-03 15:53:13 -04003740 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003741 // Already checked mNativeClass, so we do not need to check it
3742 // again.
3743 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
3744 return true;
3745 }
3746 // Bubble up the key event as WebView doesn't handle it
3747 return false;
3748 }
3749
Cary Clark843bbb82009-04-20 16:03:31 -04003750 if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
3751 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
3752 // turn off copy select if a shift-key combo is pressed
3753 mExtendSelection = mShiftIsPressed = false;
3754 if (mTouchMode == TOUCH_SELECT_MODE) {
3755 mTouchMode = TOUCH_INIT_MODE;
3756 }
3757 }
3758
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003759 if (getSettings().getNavDump()) {
3760 switch (keyCode) {
3761 case KeyEvent.KEYCODE_4:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003762 dumpDisplayTree();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003763 break;
3764 case KeyEvent.KEYCODE_5:
3765 case KeyEvent.KEYCODE_6:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003766 dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003767 break;
3768 case KeyEvent.KEYCODE_7:
3769 case KeyEvent.KEYCODE_8:
Grace Kloba8ae1b412009-11-23 10:35:34 -08003770 dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003771 break;
3772 case KeyEvent.KEYCODE_9:
3773 nativeInstrumentReport();
3774 return true;
3775 }
3776 }
3777
Derek Sollenberger718d69f2009-10-19 15:56:43 -04003778 if (nativeCursorIsTextInput()) {
Leon Scroggins1cc24202009-06-16 10:10:28 -04003779 // This message will put the node in focus, for the DOM's notion
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003780 // of focus, and make the focuscontroller active
Leon Scroggins01058282009-07-30 16:33:56 -04003781 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
3782 nativeCursorNodePointer());
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003783 // This will bring up the WebTextView and put it in focus, for
3784 // our view system's notion of focus
3785 rebuildWebTextView();
3786 // Now we need to pass the event to it
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003787 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003788 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003789 return mWebTextView.dispatchKeyEvent(event);
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04003790 }
Leon Scroggins40981262009-07-01 10:57:47 -04003791 } else if (nativeHasFocusNode()) {
3792 // In this case, the cursor is not on a text input, but the focus
3793 // might be. Check it, and if so, hand over to the WebTextView.
3794 rebuildWebTextView();
3795 if (inEditingMode()) {
Leon Scroggins0aa341f2010-02-24 16:49:35 -05003796 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003797 return mWebTextView.dispatchKeyEvent(event);
Leon Scroggins40981262009-07-01 10:57:47 -04003798 }
Cary Clark19436562009-06-04 16:25:07 -04003799 }
3800
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003801 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003802 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003803 // pass the key to DOM
3804 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
3805 // return true as DOM handles the key
3806 return true;
3807 }
3808
3809 // Bubble up the key event as WebView doesn't handle it
3810 return false;
3811 }
3812
3813 @Override
3814 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003815 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003816 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003817 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003818 }
3819
3820 if (mNativeClass == 0) {
3821 return false;
3822 }
3823
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003824 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04003825 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
3826 String text = nativeCursorText();
3827 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003828 && text.startsWith(SCHEME_TEL)) {
3829 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
3830 getContext().startActivity(intent);
3831 return true;
3832 }
3833 }
3834
3835 // Bubble up the key event if
3836 // 1. it is a system key; or
3837 // 2. the host application wants to handle it;
3838 if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
3839 return false;
3840 }
3841
Cary Clarkd6982c92009-05-29 11:02:22 -04003842 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003843 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
Leon Scroggins7a375872010-03-09 14:26:07 -05003844 if (nativeFocusIsPlugin()) {
3845 mShiftIsPressed = false;
3846 return true;
3847 } else if (commitCopy()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003848 return true;
3849 }
3850 }
3851
3852 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3853 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003854 if (nativeFocusIsPlugin()) {
3855 letPluginHandleNavKey(keyCode, event.getEventTime(), false);
3856 return true;
3857 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003858 // always handle the navigation keys in the UI thread
3859 // Bubble up the key event as WebView doesn't handle it
3860 return false;
3861 }
3862
Leon Scrogginse3225672009-06-03 15:53:13 -04003863 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003864 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04003865 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
3866 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003867
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05003868 if (mShiftIsPressed && !nativeFocusIsPlugin()) {
Cary Clarkc05af372009-10-16 10:52:27 -04003869 if (mExtendSelection) {
3870 commitCopy();
3871 } else {
3872 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04003873 invalidate(); // draw the i-beam instead of the arrow
Cary Clarkc05af372009-10-16 10:52:27 -04003874 }
3875 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003876 }
Grace Klobadd817492009-09-14 10:17:06 -07003877
3878 // perform the single click
3879 Rect visibleRect = sendOurVisibleRect();
3880 // Note that sendOurVisibleRect calls viewToContent, so the
3881 // coordinates should be in content coordinates.
3882 if (!nativeCursorIntersects(visibleRect)) {
3883 return false;
3884 }
Grace Klobadd817492009-09-14 10:17:06 -07003885 WebViewCore.CursorData data = cursorData();
3886 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
3887 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003888 if (nativeCursorIsTextInput()) {
3889 rebuildWebTextView();
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003890 centerKeyPressOnTextField();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003891 if (inEditingMode()) {
3892 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04003893 }
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003894 return true;
3895 }
Leon Scroggins6088e832010-02-17 13:17:32 -05003896 clearTextEntry(true);
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04003897 nativeSetFollowedLink(true);
3898 if (!mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
Grace Klobadd817492009-09-14 10:17:06 -07003899 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
3900 nativeCursorNodePointer());
3901 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003902 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003903 }
3904
3905 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003906 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003907 // pass the key to DOM
3908 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
3909 // return true as DOM handles the key
3910 return true;
3911 }
3912
3913 // Bubble up the key event as WebView doesn't handle it
3914 return false;
3915 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003916
Cary Clark09e383c2009-10-26 16:43:58 -04003917 private void setUpSelectXY() {
3918 mExtendSelection = false;
3919 mShiftIsPressed = true;
3920 if (nativeHasCursorNode()) {
3921 Rect rect = nativeCursorNodeBounds();
3922 mSelectX = contentToViewX(rect.left);
3923 mSelectY = contentToViewY(rect.top);
3924 } else if (mLastTouchY > getVisibleTitleHeight()) {
3925 mSelectX = mScrollX + (int) mLastTouchX;
3926 mSelectY = mScrollY + (int) mLastTouchY;
3927 } else {
3928 mSelectX = mScrollX + getViewWidth() / 2;
3929 mSelectY = mScrollY + getViewHeightWithTitle() / 2;
3930 }
3931 nativeHideCursor();
3932 }
3933
Cary Clark966641a2010-03-04 08:41:56 -05003934 /**
3935 * Use this method to put the WebView into text selection mode.
3936 * Do not rely on this functionality; it will be deprecated in the future.
3937 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003938 public void emulateShiftHeld() {
Cary Clark7f970112009-10-15 15:29:08 -04003939 if (0 == mNativeClass) return; // client isn't initialized
Cary Clark09e383c2009-10-26 16:43:58 -04003940 setUpSelectXY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003941 }
3942
3943 private boolean commitCopy() {
3944 boolean copiedSomething = false;
3945 if (mExtendSelection) {
Cary Clark57d2c3a2009-12-23 13:57:15 -05003946 String selection = nativeGetSelection();
3947 if (selection != "") {
3948 if (DebugFlags.WEB_VIEW) {
3949 Log.v(LOGTAG, "commitCopy \"" + selection + "\"");
3950 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003951 Toast.makeText(mContext
3952 , com.android.internal.R.string.text_copied
3953 , Toast.LENGTH_SHORT).show();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003954 copiedSomething = true;
Cary Clark57d2c3a2009-12-23 13:57:15 -05003955 try {
3956 IClipboard clip = IClipboard.Stub.asInterface(
3957 ServiceManager.getService("clipboard"));
3958 clip.setClipboardText(selection);
3959 } catch (android.os.RemoteException e) {
3960 Log.e(LOGTAG, "Clipboard failed", e);
3961 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003962 }
3963 mExtendSelection = false;
3964 }
3965 mShiftIsPressed = false;
Cary Clark09e383c2009-10-26 16:43:58 -04003966 invalidate(); // remove selection region and pointer
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003967 if (mTouchMode == TOUCH_SELECT_MODE) {
3968 mTouchMode = TOUCH_INIT_MODE;
3969 }
3970 return copiedSomething;
3971 }
3972
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003973 @Override
3974 protected void onAttachedToWindow() {
3975 super.onAttachedToWindow();
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003976 if (hasWindowFocus()) onWindowFocusChanged(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003977 }
3978
3979 @Override
3980 protected void onDetachedFromWindow() {
Leon Scroggins6088e832010-02-17 13:17:32 -05003981 clearTextEntry(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003982 super.onDetachedFromWindow();
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003983 // Clean up the zoom controller
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003984 mZoomButtonsController.setVisible(false);
3985 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003986
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003987 /**
3988 * @deprecated WebView no longer needs to implement
3989 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
3990 */
3991 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003992 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04003993
Leon Scrogginsa57632f2009-11-16 10:51:12 -05003994 /**
3995 * @deprecated WebView no longer needs to implement
3996 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
3997 */
3998 @Deprecated
3999 public void onChildViewRemoved(View p, View child) {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004000
4001 /**
4002 * @deprecated WebView should not have implemented
4003 * ViewTreeObserver.OnGlobalFocusChangeListener. This method
4004 * does nothing now.
4005 */
4006 @Deprecated
4007 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
4008 }
4009
Cary Clarkd6982c92009-05-29 11:02:22 -04004010 // To avoid drawing the cursor ring, and remove the TextView when our window
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004011 // loses focus.
4012 @Override
4013 public void onWindowFocusChanged(boolean hasWindowFocus) {
4014 if (hasWindowFocus) {
4015 if (hasFocus()) {
4016 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004017 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04004018 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004019 if (mNativeClass != 0) {
4020 nativeRecordButtons(true, false, true);
Leon Scroggins8cdad882009-06-30 08:47:04 -04004021 if (inEditingMode()) {
4022 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
4023 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004024 }
4025 } else {
4026 // If our window gained focus, but we do not have it, do not
Cary Clarkd6982c92009-05-29 11:02:22 -04004027 // draw the cursor ring.
4028 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004029 // We do not call nativeRecordButtons here because we assume
4030 // that when we lost focus, or window focus, it got called with
4031 // false for the first parameter
4032 }
4033 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07004034 if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004035 /*
4036 * The zoom controls come in their own window, so our window
Cary Clarkd6982c92009-05-29 11:02:22 -04004037 * loses focus. Our policy is to not draw the cursor ring if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004038 * our window is not focused, but this is an exception since
4039 * the user can still navigate the web page with the zoom
4040 * controls showing.
4041 */
Cary Clarkd6982c92009-05-29 11:02:22 -04004042 // If our window has lost focus, stop drawing the cursor ring
4043 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004044 }
4045 mGotKeyDown = false;
4046 mShiftIsPressed = false;
4047 if (mNativeClass != 0) {
4048 nativeRecordButtons(false, false, true);
4049 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004050 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004051 }
4052 invalidate();
4053 super.onWindowFocusChanged(hasWindowFocus);
4054 }
4055
Leon Scroggins63dda1c2009-04-15 13:25:00 -04004056 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004057 * Pass a message to WebCore Thread, telling the WebCore::Page's
4058 * FocusController to be "inactive" so that it will
4059 * not draw the blinking cursor. It gets set to "active" to draw the cursor
4060 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04004061 */
Leon Scroggins01058282009-07-30 16:33:56 -04004062 /* package */ void setFocusControllerInactive() {
Leon Scrogginsfd06bc82009-06-08 13:22:02 -04004063 // Do not need to also check whether mWebViewCore is null, because
4064 // mNativeClass is only set if mWebViewCore is non null
4065 if (mNativeClass == 0) return;
Leon Scroggins8cdad882009-06-30 08:47:04 -04004066 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
Leon Scroggins63dda1c2009-04-15 13:25:00 -04004067 }
4068
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004069 @Override
4070 protected void onFocusChanged(boolean focused, int direction,
4071 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004072 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004073 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
4074 }
4075 if (focused) {
4076 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004077 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004078 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004079 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004080 if (mNativeClass != 0) {
4081 nativeRecordButtons(true, false, true);
4082 }
4083 //} else {
4084 // The WebView has gained focus while we do not have
4085 // windowfocus. When our window lost focus, we should have
4086 // called nativeRecordButtons(false...)
4087 }
4088 } else {
4089 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04004090 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004091 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004092 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004093 if (mNativeClass != 0) {
4094 nativeRecordButtons(false, false, true);
4095 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04004096 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004097 }
4098 mGotKeyDown = false;
4099 }
4100
4101 super.onFocusChanged(focused, direction, previouslyFocusedRect);
4102 }
4103
Grace Kloba3f9faf42009-10-13 14:13:54 -07004104 /**
4105 * @hide
4106 */
4107 @Override
4108 protected boolean setFrame(int left, int top, int right, int bottom) {
4109 boolean changed = super.setFrame(left, top, right, bottom);
4110 if (!changed && mHeightCanMeasure) {
4111 // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
4112 // in WebViewCore after we get the first layout. We do call
4113 // requestLayout() when we get contentSizeChanged(). But the View
4114 // system won't call onSizeChanged if the dimension is not changed.
4115 // In this case, we need to call sendViewSizeZoom() explicitly to
4116 // notify the WebKit about the new dimensions.
4117 sendViewSizeZoom();
4118 }
4119 return changed;
4120 }
4121
Grace Kloba3a0def22010-01-23 21:11:54 -08004122 private static class PostScale implements Runnable {
4123 final WebView mWebView;
4124 final boolean mUpdateTextWrap;
4125
4126 public PostScale(WebView webView, boolean updateTextWrap) {
4127 mWebView = webView;
4128 mUpdateTextWrap = updateTextWrap;
4129 }
4130
4131 public void run() {
4132 if (mWebView.mWebViewCore != null) {
4133 // we always force, in case our height changed, in which case we
4134 // still want to send the notification over to webkit.
4135 mWebView.setNewZoomScale(mWebView.mActualScale,
4136 mUpdateTextWrap, true);
Grace Klobab3d0cc52010-03-01 12:25:29 -08004137 // update the zoom buttons as the scale can be changed
4138 if (mWebView.getSettings().getBuiltInZoomControls()) {
4139 mWebView.updateZoomButtonsEnabled();
4140 }
Grace Kloba3a0def22010-01-23 21:11:54 -08004141 }
4142 }
4143 }
4144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004145 @Override
4146 protected void onSizeChanged(int w, int h, int ow, int oh) {
4147 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004148 // Center zooming to the center of the screen.
Cary Clarka91874d2009-08-24 14:08:43 -04004149 if (mZoomScale == 0) { // unless we're already zooming
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08004150 // To anchor at top left corner.
4151 mZoomCenterX = 0;
4152 mZoomCenterY = getVisibleTitleHeight();
Grace Kloba3a0def22010-01-23 21:11:54 -08004153 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
4154 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Cary Clarka91874d2009-08-24 14:08:43 -04004155 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004156
Grace Klobaa4fa1072009-11-09 12:01:50 -08004157 // adjust the max viewport width depending on the view dimensions. This
4158 // is to ensure the scaling is not going insane. So do not shrink it if
4159 // the view size is temporarily smaller, e.g. when soft keyboard is up.
4160 int newMaxViewportWidth = (int) (Math.max(w, h) / DEFAULT_MIN_ZOOM_SCALE);
4161 if (newMaxViewportWidth > sMaxViewportWidth) {
4162 sMaxViewportWidth = newMaxViewportWidth;
4163 }
4164
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004165 // update mMinZoomScale if the minimum zoom scale is not fixed
4166 if (!mMinZoomScaleFixed) {
Grace Klobaba672802009-09-25 15:54:08 -07004167 // when change from narrow screen to wide screen, the new viewWidth
4168 // can be wider than the old content width. We limit the minimum
4169 // scale to 1.0f. The proper minimum scale will be calculated when
4170 // the new picture shows up.
4171 mMinZoomScale = Math.min(1.0f, (float) getViewWidth()
Grace Klobae397a882009-08-06 12:04:14 -07004172 / (mDrawHistory ? mHistoryPicture.getWidth()
Grace Klobaba672802009-09-25 15:54:08 -07004173 : mZoomOverviewWidth));
Grace Kloba16efce72009-11-10 15:49:03 -08004174 if (mInitialScaleInPercent > 0) {
4175 // limit the minZoomScale to the initialScale if it is set
4176 float initialScale = mInitialScaleInPercent / 100.0f;
4177 if (mMinZoomScale > initialScale) {
4178 mMinZoomScale = initialScale;
4179 }
4180 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004181 }
4182
Grace Kloba70b942d2009-12-11 19:33:04 -08004183 // onSizeChanged() is called during WebView layout. And any
4184 // requestLayout() is blocked during layout. As setNewZoomScale() will
4185 // call its child View to reposition itself through ViewManager's
4186 // scaleAll(), we need to post a Runnable to ensure requestLayout().
Grace Kloba3a0def22010-01-23 21:11:54 -08004187 // <b/>
4188 // only update the text wrap scale if width changed.
4189 post(new PostScale(this, w != ow));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004190 }
4191
4192 @Override
4193 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
4194 super.onScrollChanged(l, t, oldl, oldt);
4195 sendOurVisibleRect();
Grace Kloba4addf9b2010-03-02 10:19:53 -08004196 // update WebKit if visible title bar height changed. The logic is same
4197 // as getVisibleTitleHeight.
4198 int titleHeight = getTitleHeight();
4199 if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
4200 sendViewSizeZoom();
4201 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004202 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004203
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004204 @Override
4205 public boolean dispatchKeyEvent(KeyEvent event) {
4206 boolean dispatch = true;
4207
Leon Scroggins7a375872010-03-09 14:26:07 -05004208 // Textfields and plugins need to receive the shift up key even if
4209 // another key was released while the shift key was held down.
4210 if (!inEditingMode() && !nativeFocusIsPlugin()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004211 if (event.getAction() == KeyEvent.ACTION_DOWN) {
4212 mGotKeyDown = true;
4213 } else {
4214 if (!mGotKeyDown) {
4215 /*
4216 * We got a key up for which we were not the recipient of
4217 * the original key down. Don't give it to the view.
4218 */
4219 dispatch = false;
4220 }
4221 mGotKeyDown = false;
4222 }
4223 }
4224
4225 if (dispatch) {
4226 return super.dispatchKeyEvent(event);
4227 } else {
4228 // We didn't dispatch, so let something else handle the key
4229 return false;
4230 }
4231 }
4232
4233 // Here are the snap align logic:
4234 // 1. If it starts nearly horizontally or vertically, snap align;
4235 // 2. If there is a dramitic direction change, let it go;
4236 // 3. If there is a same direction back and forth, lock it.
4237
4238 // adjustable parameters
4239 private int mMinLockSnapReverseDistance;
4240 private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
4241 private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
4242
Mike Reed19f3f0e2009-11-12 12:50:20 -05004243 private static int sign(float x) {
4244 return x > 0 ? 1 : (x < 0 ? -1 : 0);
4245 }
4246
4247 // if the page can scroll <= this value, we won't allow the drag tracker
4248 // to have any effect.
4249 private static final int MIN_SCROLL_AMOUNT_TO_DISABLE_DRAG_TRACKER = 4;
4250
4251 private class DragTrackerHandler {
4252 private final DragTracker mProxy;
4253 private final float mStartY, mStartX;
4254 private final float mMinDY, mMinDX;
4255 private final float mMaxDY, mMaxDX;
4256 private float mCurrStretchY, mCurrStretchX;
4257 private int mSX, mSY;
Mike Reedc95326092010-02-08 16:49:58 -05004258 private Interpolator mInterp;
4259 private float[] mXY = new float[2];
4260
4261 // inner (non-state) classes can't have enums :(
4262 private static final int DRAGGING_STATE = 0;
4263 private static final int ANIMATING_STATE = 1;
4264 private static final int FINISHED_STATE = 2;
4265 private int mState;
Mike Reed19f3f0e2009-11-12 12:50:20 -05004266
4267 public DragTrackerHandler(float x, float y, DragTracker proxy) {
4268 mProxy = proxy;
4269
4270 int docBottom = computeVerticalScrollRange() + getTitleHeight();
4271 int viewTop = getScrollY();
4272 int viewBottom = viewTop + getHeight();
4273
4274 mStartY = y;
4275 mMinDY = -viewTop;
4276 mMaxDY = docBottom - viewBottom;
4277
Mike Reedfdc54242010-01-08 10:29:51 -05004278 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004279 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " dragtracker y= " + y +
4280 " up/down= " + mMinDY + " " + mMaxDY);
4281 }
4282
4283 int docRight = computeHorizontalScrollRange();
4284 int viewLeft = getScrollX();
4285 int viewRight = viewLeft + getWidth();
4286 mStartX = x;
4287 mMinDX = -viewLeft;
4288 mMaxDX = docRight - viewRight;
4289
Mike Reedc95326092010-02-08 16:49:58 -05004290 mState = DRAGGING_STATE;
Mike Reed19f3f0e2009-11-12 12:50:20 -05004291 mProxy.onStartDrag(x, y);
4292
4293 // ensure we buildBitmap at least once
4294 mSX = -99999;
4295 }
4296
4297 private float computeStretch(float delta, float min, float max) {
4298 float stretch = 0;
4299 if (max - min > MIN_SCROLL_AMOUNT_TO_DISABLE_DRAG_TRACKER) {
4300 if (delta < min) {
4301 stretch = delta - min;
4302 } else if (delta > max) {
4303 stretch = delta - max;
4304 }
4305 }
4306 return stretch;
4307 }
4308
4309 public void dragTo(float x, float y) {
4310 float sy = computeStretch(mStartY - y, mMinDY, mMaxDY);
4311 float sx = computeStretch(mStartX - x, mMinDX, mMaxDX);
4312
Mike Reedc95326092010-02-08 16:49:58 -05004313 if ((mSnapScrollMode & SNAP_X) != 0) {
4314 sy = 0;
4315 } else if ((mSnapScrollMode & SNAP_Y) != 0) {
4316 sx = 0;
4317 }
4318
Mike Reed19f3f0e2009-11-12 12:50:20 -05004319 if (mCurrStretchX != sx || mCurrStretchY != sy) {
4320 mCurrStretchX = sx;
4321 mCurrStretchY = sy;
Mike Reedfdc54242010-01-08 10:29:51 -05004322 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004323 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "---- stretch " + sx +
4324 " " + sy);
4325 }
4326 if (mProxy.onStretchChange(sx, sy)) {
4327 invalidate();
4328 }
4329 }
4330 }
4331
4332 public void stopDrag() {
Mike Reedc95326092010-02-08 16:49:58 -05004333 final int DURATION = 200;
4334 int now = (int)SystemClock.uptimeMillis();
4335 mInterp = new Interpolator(2);
4336 mXY[0] = mCurrStretchX;
4337 mXY[1] = mCurrStretchY;
4338 // float[] blend = new float[] { 0.5f, 0, 0.75f, 1 };
4339 float[] blend = new float[] { 0, 0.5f, 0.75f, 1 };
4340 mInterp.setKeyFrame(0, now, mXY, blend);
4341 float[] zerozero = new float[] { 0, 0 };
4342 mInterp.setKeyFrame(1, now + DURATION, zerozero, null);
4343 mState = ANIMATING_STATE;
4344
Mike Reedfdc54242010-01-08 10:29:51 -05004345 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reedc95326092010-02-08 16:49:58 -05004346 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "----- stopDrag, starting animation");
Mike Reed19f3f0e2009-11-12 12:50:20 -05004347 }
Mike Reedc95326092010-02-08 16:49:58 -05004348 }
4349
4350 // Call this after each draw. If it ruturns null, the tracker is done
4351 public boolean isFinished() {
4352 return mState == FINISHED_STATE;
Mike Reed19f3f0e2009-11-12 12:50:20 -05004353 }
4354
4355 private int hiddenHeightOfTitleBar() {
4356 return getTitleHeight() - getVisibleTitleHeight();
4357 }
4358
4359 // need a way to know if 565 or 8888 is the right config for
4360 // capturing the display and giving it to the drag proxy
4361 private Bitmap.Config offscreenBitmapConfig() {
4362 // hard code 565 for now
4363 return Bitmap.Config.RGB_565;
4364 }
4365
4366 /* If the tracker draws, then this returns true, otherwise it will
4367 return false, and draw nothing.
4368 */
4369 public boolean draw(Canvas canvas) {
4370 if (mCurrStretchX != 0 || mCurrStretchY != 0) {
4371 int sx = getScrollX();
4372 int sy = getScrollY() - hiddenHeightOfTitleBar();
Mike Reed19f3f0e2009-11-12 12:50:20 -05004373 if (mSX != sx || mSY != sy) {
4374 buildBitmap(sx, sy);
4375 mSX = sx;
4376 mSY = sy;
4377 }
4378
Mike Reedc95326092010-02-08 16:49:58 -05004379 if (mState == ANIMATING_STATE) {
4380 Interpolator.Result result = mInterp.timeToValues(mXY);
4381 if (result == Interpolator.Result.FREEZE_END) {
4382 mState = FINISHED_STATE;
4383 return false;
4384 } else {
4385 mProxy.onStretchChange(mXY[0], mXY[1]);
4386 invalidate();
4387 // fall through to the draw
4388 }
4389 }
Mike Reed19f3f0e2009-11-12 12:50:20 -05004390 int count = canvas.save(Canvas.MATRIX_SAVE_FLAG);
4391 canvas.translate(sx, sy);
4392 mProxy.onDraw(canvas);
4393 canvas.restoreToCount(count);
4394 return true;
4395 }
Mike Reedfdc54242010-01-08 10:29:51 -05004396 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004397 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, " -- draw false " +
4398 mCurrStretchX + " " + mCurrStretchY);
4399 }
4400 return false;
4401 }
4402
4403 private void buildBitmap(int sx, int sy) {
4404 int w = getWidth();
4405 int h = getViewHeight();
4406 Bitmap bm = Bitmap.createBitmap(w, h, offscreenBitmapConfig());
4407 Canvas canvas = new Canvas(bm);
4408 canvas.translate(-sx, -sy);
4409 drawContent(canvas);
4410
Mike Reedfdc54242010-01-08 10:29:51 -05004411 if (DebugFlags.DRAG_TRACKER || DEBUG_DRAG_TRACKER) {
Mike Reed19f3f0e2009-11-12 12:50:20 -05004412 Log.d(DebugFlags.DRAG_TRACKER_LOGTAG, "--- buildBitmap " + sx +
4413 " " + sy + " " + w + " " + h);
4414 }
4415 mProxy.onBitmapChange(bm);
4416 }
4417 }
4418
4419 /** @hide */
4420 public static class DragTracker {
4421 public void onStartDrag(float x, float y) {}
4422 public boolean onStretchChange(float sx, float sy) {
4423 // return true to have us inval the view
4424 return false;
4425 }
4426 public void onStopDrag() {}
4427 public void onBitmapChange(Bitmap bm) {}
4428 public void onDraw(Canvas canvas) {}
4429 }
4430
4431 /** @hide */
4432 public DragTracker getDragTracker() {
4433 return mDragTracker;
4434 }
4435
4436 /** @hide */
4437 public void setDragTracker(DragTracker tracker) {
4438 mDragTracker = tracker;
4439 }
4440
4441 private DragTracker mDragTracker;
4442 private DragTrackerHandler mDragTrackerHandler;
4443
Grace Kloba3a0def22010-01-23 21:11:54 -08004444 private class ScaleDetectorListener implements
4445 ScaleGestureDetector.OnScaleGestureListener {
4446
4447 public boolean onScaleBegin(ScaleGestureDetector detector) {
4448 // cancel the single touch handling
4449 cancelTouch();
4450 if (mZoomButtonsController.isVisible()) {
4451 mZoomButtonsController.setVisible(false);
4452 }
4453 // reset the zoom overview mode so that the page won't auto grow
4454 mInZoomOverview = false;
4455 // If it is in password mode, turn it off so it does not draw
4456 // misplaced.
4457 if (inEditingMode() && nativeFocusCandidateIsPassword()) {
4458 mWebTextView.setInPassword(false);
4459 }
4460 return true;
4461 }
4462
4463 public void onScaleEnd(ScaleGestureDetector detector) {
4464 if (mPreviewZoomOnly) {
4465 mPreviewZoomOnly = false;
4466 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
4467 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
4468 // don't reflow when zoom in; when zoom out, do reflow if the
4469 // new scale is almost minimum scale;
Grace Klobac6f95fe2010-03-10 13:25:34 -08004470 boolean reflowNow = (mActualScale - mMinZoomScale
4471 <= MINIMUM_SCALE_INCREMENT)
Grace Kloba3a0def22010-01-23 21:11:54 -08004472 || ((mActualScale <= 0.8 * mTextWrapScale));
4473 // force zoom after mPreviewZoomOnly is set to false so that the
4474 // new view size will be passed to the WebKit
4475 setNewZoomScale(mActualScale, reflowNow, true);
4476 // call invalidate() to draw without zoom filter
4477 invalidate();
4478 }
4479 // adjust the edit text view if needed
4480 if (inEditingMode() && didUpdateTextViewBounds(false)
4481 && nativeFocusCandidateIsPassword()) {
4482 // If it is a password field, start drawing the
4483 // WebTextView once again.
4484 mWebTextView.setInPassword(true);
4485 }
4486 // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as it
4487 // may trigger the unwanted click, can't use TOUCH_DRAG_MODE as it
4488 // may trigger the unwanted fling.
4489 mTouchMode = TOUCH_PINCH_DRAG;
Grace Klobac2242f22010-03-05 14:00:26 -08004490 mConfirmMove = true;
Grace Kloba3a0def22010-01-23 21:11:54 -08004491 startTouch(detector.getFocusX(), detector.getFocusY(),
4492 mLastTouchTime);
4493 }
4494
4495 public boolean onScale(ScaleGestureDetector detector) {
4496 float scale = (float) (Math.round(detector.getScaleFactor()
4497 * mActualScale * 100) / 100.0);
Grace Klobac6f95fe2010-03-10 13:25:34 -08004498 if (Math.abs(scale - mActualScale) >= MINIMUM_SCALE_INCREMENT) {
Grace Kloba3a0def22010-01-23 21:11:54 -08004499 mPreviewZoomOnly = true;
4500 // limit the scale change per step
4501 if (scale > mActualScale) {
4502 scale = Math.min(scale, mActualScale * 1.25f);
4503 } else {
4504 scale = Math.max(scale, mActualScale * 0.8f);
4505 }
4506 mZoomCenterX = detector.getFocusX();
4507 mZoomCenterY = detector.getFocusY();
4508 setNewZoomScale(scale, false, false);
4509 invalidate();
4510 return true;
4511 }
4512 return false;
4513 }
4514 }
4515
Grace Klobac2242f22010-03-05 14:00:26 -08004516 private boolean hitFocusedPlugin(int contentX, int contentY) {
4517 return nativeFocusIsPlugin()
4518 && nativePointInNavCache(contentX, contentY, mNavSlop)
4519 && nativeCacheHitNodePointer() == nativeFocusNodePointer();
4520 }
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(),
5017 getViewWidth() / 3, getViewHeight() / 3);
5018 }
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 }
5115 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005116 if (mShiftIsPressed && !nativeFocusIsPlugin()) {
Cary Clarkbadd8392009-10-15 13:32:08 -04005117 return true; // discard press if copy in progress
5118 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005119 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04005120 if (mNativeClass == 0) {
5121 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005122 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04005123 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005124 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
5125 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
5126 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005127 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005128 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005129 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04005130 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005131 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005132 }
5133 if (isInTouchMode()) requestFocusFromTouch();
5134 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04005135 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005136 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04005137 // LONG_PRESS_CENTER is set in common onKeyDown
5138 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005139 mTrackballDown = false;
5140 mTrackballUpTime = time;
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005141 if (mShiftIsPressed && !nativeFocusIsPlugin()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005142 if (mExtendSelection) {
5143 commitCopy();
5144 } else {
5145 mExtendSelection = true;
Cary Clark09e383c2009-10-26 16:43:58 -04005146 invalidate(); // draw the i-beam instead of the arrow
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005147 }
Cary Clarkbadd8392009-10-15 13:32:08 -04005148 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005149 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005150 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005151 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04005152 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005153 );
5154 }
5155 return false; // let common code in onKeyUp at it
5156 }
5157 if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005158 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005159 return false;
5160 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005161 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005162 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005163 return true; // discard move if trackball is down
5164 }
5165 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005166 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005167 return true;
5168 }
5169 // TODO: alternatively we can do panning as touch does
5170 switchOutDrawHistory();
5171 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005172 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005173 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005174 + time + " last=" + mTrackballLastTime);
5175 }
5176 mTrackballFirstTime = time;
5177 mTrackballXMove = mTrackballYMove = 0;
5178 }
5179 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005180 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005181 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
5182 }
5183 mTrackballRemainsX += ev.getX();
5184 mTrackballRemainsY += ev.getY();
5185 doTrackball(time);
5186 return true;
5187 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005188
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005189 void moveSelection(float xRate, float yRate) {
5190 if (mNativeClass == 0)
5191 return;
5192 int width = getViewWidth();
5193 int height = getViewHeight();
Cary Clarkc05af372009-10-16 10:52:27 -04005194 mSelectX += xRate;
5195 mSelectY += yRate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005196 int maxX = width + mScrollX;
5197 int maxY = height + mScrollY;
5198 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
5199 , mSelectX));
5200 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
5201 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005202 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005203 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005204 + " mSelectX=" + mSelectX
5205 + " mSelectY=" + mSelectY
5206 + " mScrollX=" + mScrollX
5207 + " mScrollY=" + mScrollY
5208 + " xRate=" + xRate
5209 + " yRate=" + yRate
5210 );
5211 }
Leon Scroggins0236e672009-09-02 21:12:08 -04005212 nativeMoveSelection(viewToContentX(mSelectX),
5213 viewToContentY(mSelectY), mExtendSelection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005214 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04005215 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005216 : 0;
5217 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04005218 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005219 : 0;
5220 pinScrollBy(scrollX, scrollY, true, 0);
5221 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
5222 requestRectangleOnScreen(select);
5223 invalidate();
5224 }
5225
5226 private int scaleTrackballX(float xRate, int width) {
5227 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
5228 int nextXMove = xMove;
5229 if (xMove > 0) {
5230 if (xMove > mTrackballXMove) {
5231 xMove -= mTrackballXMove;
5232 }
5233 } else if (xMove < mTrackballXMove) {
5234 xMove -= mTrackballXMove;
5235 }
5236 mTrackballXMove = nextXMove;
5237 return xMove;
5238 }
5239
5240 private int scaleTrackballY(float yRate, int height) {
5241 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
5242 int nextYMove = yMove;
5243 if (yMove > 0) {
5244 if (yMove > mTrackballYMove) {
5245 yMove -= mTrackballYMove;
5246 }
5247 } else if (yMove < mTrackballYMove) {
5248 yMove -= mTrackballYMove;
5249 }
5250 mTrackballYMove = nextYMove;
5251 return yMove;
5252 }
5253
5254 private int keyCodeToSoundsEffect(int keyCode) {
5255 switch(keyCode) {
5256 case KeyEvent.KEYCODE_DPAD_UP:
5257 return SoundEffectConstants.NAVIGATION_UP;
5258 case KeyEvent.KEYCODE_DPAD_RIGHT:
5259 return SoundEffectConstants.NAVIGATION_RIGHT;
5260 case KeyEvent.KEYCODE_DPAD_DOWN:
5261 return SoundEffectConstants.NAVIGATION_DOWN;
5262 case KeyEvent.KEYCODE_DPAD_LEFT:
5263 return SoundEffectConstants.NAVIGATION_LEFT;
5264 }
5265 throw new IllegalArgumentException("keyCode must be one of " +
5266 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
5267 "KEYCODE_DPAD_LEFT}.");
5268 }
5269
5270 private void doTrackball(long time) {
5271 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
5272 if (elapsed == 0) {
5273 elapsed = TRACKBALL_TIMEOUT;
5274 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005275 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005276 float yRate = mTrackballRemainsY * 1000 / elapsed;
Cary Clarkc05af372009-10-16 10:52:27 -04005277 int viewWidth = getViewWidth();
5278 int viewHeight = getViewHeight();
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005279 if (mShiftIsPressed && !nativeFocusIsPlugin()) {
Cary Clarkc05af372009-10-16 10:52:27 -04005280 moveSelection(scaleTrackballX(xRate, viewWidth),
5281 scaleTrackballY(yRate, viewHeight));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005282 mTrackballRemainsX = mTrackballRemainsY = 0;
5283 return;
5284 }
5285 float ax = Math.abs(xRate);
5286 float ay = Math.abs(yRate);
5287 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005288 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005289 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
5290 + " xRate=" + xRate
5291 + " yRate=" + yRate
5292 + " mTrackballRemainsX=" + mTrackballRemainsX
5293 + " mTrackballRemainsY=" + mTrackballRemainsY);
5294 }
Cary Clarkc05af372009-10-16 10:52:27 -04005295 int width = mContentWidth - viewWidth;
5296 int height = mContentHeight - viewHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005297 if (width < 0) width = 0;
5298 if (height < 0) height = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005299 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
5300 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
5301 maxA = Math.max(ax, ay);
5302 int count = Math.max(0, (int) maxA);
5303 int oldScrollX = mScrollX;
5304 int oldScrollY = mScrollY;
5305 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005306 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
5307 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005308 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
5309 KeyEvent.KEYCODE_DPAD_RIGHT;
5310 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005311 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005312 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005313 + " count=" + count
5314 + " mTrackballRemainsX=" + mTrackballRemainsX
5315 + " mTrackballRemainsY=" + mTrackballRemainsY);
5316 }
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005317 if (nativeFocusIsPlugin()) {
5318 for (int i = 0; i < count; i++) {
5319 letPluginHandleNavKey(selectKeyCode, time, true);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005320 }
Leon Scroggins3dc02fe2010-03-09 11:27:34 -05005321 letPluginHandleNavKey(selectKeyCode, time, false);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005322 } else if (navHandledKey(selectKeyCode, count, false, time)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005323 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
5324 }
5325 mTrackballRemainsX = mTrackballRemainsY = 0;
5326 }
5327 if (count >= TRACKBALL_SCROLL_COUNT) {
5328 int xMove = scaleTrackballX(xRate, width);
5329 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005330 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005331 Log.v(LOGTAG, "doTrackball pinScrollBy"
5332 + " count=" + count
5333 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04005334 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
5335 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005336 );
5337 }
5338 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
5339 xMove = 0;
5340 }
5341 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
5342 yMove = 0;
5343 }
5344 if (xMove != 0 || yMove != 0) {
5345 pinScrollBy(xMove, yMove, true, 0);
5346 }
5347 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04005348 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005349 }
5350
Grace Klobad7625dd2010-03-04 11:46:12 -08005351 private int computeMaxScrollX() {
5352 return Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
5353 }
5354
Mike Reede8853fc2009-09-04 14:01:48 -04005355 private int computeMaxScrollY() {
Grace Klobad7625dd2010-03-04 11:46:12 -08005356 return Math.max(computeVerticalScrollRange() + getTitleHeight()
5357 - getViewHeightWithTitle(), getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04005358 }
5359
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005360 public void flingScroll(int vx, int vy) {
Grace Klobad7625dd2010-03-04 11:46:12 -08005361 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
5362 computeMaxScrollY(), getViewWidth() / 3, getViewHeight() / 3);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005363 invalidate();
5364 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005365
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005366 private void doFling() {
5367 if (mVelocityTracker == null) {
5368 return;
5369 }
Grace Klobad7625dd2010-03-04 11:46:12 -08005370 int maxX = computeMaxScrollX();
Mike Reede8853fc2009-09-04 14:01:48 -04005371 int maxY = computeMaxScrollY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005372
Romain Guy4296fc42009-07-06 11:48:52 -07005373 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005374 int vx = (int) mVelocityTracker.getXVelocity();
5375 int vy = (int) mVelocityTracker.getYVelocity();
5376
5377 if (mSnapScrollMode != SNAP_NONE) {
Cary Clarkac492e12009-10-14 14:53:37 -04005378 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005379 vy = 0;
5380 } else {
5381 vx = 0;
5382 }
5383 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005384
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005385 if (true /* EMG release: make our fling more like Maps' */) {
5386 // maps cuts their velocity in half
5387 vx = vx * 3 / 4;
5388 vy = vy * 3 / 4;
5389 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04005390 if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005391 WebViewCore.resumePriority();
Grace Klobad7625dd2010-03-04 11:46:12 -08005392 if (mScroller.springback(mScrollX, mScrollY, 0, computeMaxScrollX(),
5393 0, computeMaxScrollY())) {
5394 invalidate();
5395 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04005396 return;
5397 }
Cary Clark278ce052009-08-31 16:08:42 -04005398 float currentVelocity = mScroller.getCurrVelocity();
5399 if (mLastVelocity > 0 && currentVelocity > 0) {
5400 float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
5401 - Math.atan2(vy, vx)));
5402 final float circle = (float) (Math.PI) * 2.0f;
5403 if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
5404 vx += currentVelocity * mLastVelX / mLastVelocity;
5405 vy += currentVelocity * mLastVelY / mLastVelocity;
5406 if (DebugFlags.WEB_VIEW) {
5407 Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
5408 }
5409 } else if (DebugFlags.WEB_VIEW) {
5410 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
5411 }
5412 } else if (DebugFlags.WEB_VIEW) {
5413 Log.v(LOGTAG, "doFling start last=" + mLastVelocity
Cary Clarkaa7caa62009-09-08 14:15:07 -04005414 + " current=" + currentVelocity
5415 + " vx=" + vx + " vy=" + vy
5416 + " maxX=" + maxX + " maxY=" + maxY
5417 + " mScrollX=" + mScrollX + " mScrollY=" + mScrollY);
Cary Clark278ce052009-08-31 16:08:42 -04005418 }
5419 mLastVelX = vx;
5420 mLastVelY = vy;
5421 mLastVelocity = (float) Math.hypot(vx, vy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005422
Grace Klobad7625dd2010-03-04 11:46:12 -08005423 mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY,
5424 getViewWidth() / 3, getViewHeight() / 3);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005425 // TODO: duration is calculated based on velocity, if the range is
5426 // small, the animation will stop before duration is up. We may
5427 // want to calculate how long the animation is going to run to precisely
5428 // resume the webcore update.
5429 final int time = mScroller.getDuration();
Grace Kloba96949ef2010-01-25 09:53:01 -08005430 mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_PRIORITY, time);
Mike Cleronf116bf82009-09-27 19:14:12 -07005431 awakenScrollBars(time);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005432 invalidate();
5433 }
5434
Grace Klobac6f95fe2010-03-10 13:25:34 -08005435 private boolean zoomWithPreview(float scale, boolean updateTextWrapScale) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005436 float oldScale = mActualScale;
Grace Kloba675c7d22009-07-23 09:21:21 -07005437 mInitialScrollX = mScrollX;
5438 mInitialScrollY = mScrollY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005439
Grace Kloba25737912009-06-19 12:42:47 -07005440 // snap to DEFAULT_SCALE if it is close
Grace Klobac6f95fe2010-03-10 13:25:34 -08005441 if (Math.abs(scale - mDefaultScale) < MINIMUM_SCALE_INCREMENT) {
Grace Kloba0d8b77c2009-06-25 11:20:51 -07005442 scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005443 }
5444
Grace Klobac6f95fe2010-03-10 13:25:34 -08005445 setNewZoomScale(scale, updateTextWrapScale, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005446
5447 if (oldScale != mActualScale) {
5448 // use mZoomPickerScale to see zoom preview first
5449 mZoomStart = SystemClock.uptimeMillis();
5450 mInvInitialZoomScale = 1.0f / oldScale;
5451 mInvFinalZoomScale = 1.0f / mActualScale;
5452 mZoomScale = mActualScale;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005453 WebViewCore.pauseUpdatePicture(mWebViewCore);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005454 invalidate();
5455 return true;
5456 } else {
5457 return false;
5458 }
5459 }
5460
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005461 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005462 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
5463 * in charge of installing this view to the view hierarchy. This view will
5464 * become visible when the user starts scrolling via touch and fade away if
5465 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005466 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07005467 * API version 3 introduces a built-in zoom mechanism that is shown
5468 * automatically by the MapView. This is the preferred approach for
5469 * showing the zoom UI.
5470 *
5471 * @deprecated The built-in zoom mechanism is preferred, see
5472 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005473 */
The Android Open Source Project10592532009-03-18 17:39:46 -07005474 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005475 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07005476 if (!getSettings().supportZoom()) {
5477 Log.w(LOGTAG, "This WebView doesn't support zoom.");
5478 return null;
5479 }
5480 if (mZoomControls == null) {
5481 mZoomControls = createZoomControls();
Cary Clarkd6982c92009-05-29 11:02:22 -04005482
The Android Open Source Project10592532009-03-18 17:39:46 -07005483 /*
5484 * need to be set to VISIBLE first so that getMeasuredHeight() in
5485 * {@link #onSizeChanged()} can return the measured value for proper
5486 * layout.
5487 */
5488 mZoomControls.setVisibility(View.VISIBLE);
5489 mZoomControlRunnable = new Runnable() {
5490 public void run() {
Cary Clarkd6982c92009-05-29 11:02:22 -04005491
The Android Open Source Project10592532009-03-18 17:39:46 -07005492 /* Don't dismiss the controls if the user has
5493 * focus on them. Wait and check again later.
5494 */
5495 if (!mZoomControls.hasFocus()) {
5496 mZoomControls.hide();
5497 } else {
5498 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5499 mPrivateHandler.postDelayed(mZoomControlRunnable,
5500 ZOOM_CONTROLS_TIMEOUT);
5501 }
5502 }
5503 };
5504 }
5505 return mZoomControls;
5506 }
5507
5508 private ExtendedZoomControls createZoomControls() {
5509 ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
5510 , null);
5511 zoomControls.setOnZoomInClickListener(new OnClickListener() {
5512 public void onClick(View v) {
5513 // reset time out
5514 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5515 mPrivateHandler.postDelayed(mZoomControlRunnable,
5516 ZOOM_CONTROLS_TIMEOUT);
5517 zoomIn();
5518 }
5519 });
5520 zoomControls.setOnZoomOutClickListener(new OnClickListener() {
5521 public void onClick(View v) {
5522 // reset time out
5523 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5524 mPrivateHandler.postDelayed(mZoomControlRunnable,
5525 ZOOM_CONTROLS_TIMEOUT);
5526 zoomOut();
5527 }
5528 });
The Android Open Source Project10592532009-03-18 17:39:46 -07005529 return zoomControls;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005530 }
5531
5532 /**
5533 * Gets the {@link ZoomButtonsController} which can be used to add
5534 * additional buttons to the zoom controls window.
Cary Clarkd6982c92009-05-29 11:02:22 -04005535 *
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005536 * @return The instance of {@link ZoomButtonsController} used by this class,
5537 * or null if it is unavailable.
The Android Open Source Project10592532009-03-18 17:39:46 -07005538 * @hide
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07005539 */
5540 public ZoomButtonsController getZoomButtonsController() {
5541 return mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005542 }
5543
5544 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005545 * Perform zoom in in the webview
5546 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
5547 */
5548 public boolean zoomIn() {
5549 // TODO: alternatively we can disallow this during draw history mode
5550 switchOutDrawHistory();
Grace Kloba3a0def22010-01-23 21:11:54 -08005551 mInZoomOverview = false;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005552 // Center zooming to the center of the screen.
Grace Kloba3a0def22010-01-23 21:11:54 -08005553 mZoomCenterX = getViewWidth() * .5f;
5554 mZoomCenterY = getViewHeight() * .5f;
5555 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5556 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005557 return zoomWithPreview(mActualScale * 1.25f, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005558 }
5559
5560 /**
5561 * Perform zoom out in the webview
5562 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
5563 */
5564 public boolean zoomOut() {
5565 // TODO: alternatively we can disallow this during draw history mode
5566 switchOutDrawHistory();
Grace Kloba3a0def22010-01-23 21:11:54 -08005567 // Center zooming to the center of the screen.
5568 mZoomCenterX = getViewWidth() * .5f;
5569 mZoomCenterY = getViewHeight() * .5f;
5570 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5571 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005572 return zoomWithPreview(mActualScale * 0.8f, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005573 }
5574
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005575 private void updateSelection() {
5576 if (mNativeClass == 0) {
5577 return;
5578 }
5579 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04005580 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
5581 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07005582 Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
5583 contentX + mNavSlop, contentY + mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005584 nativeSelectBestAt(rect);
5585 }
5586
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005587 /**
Leon Scroggins72543e12009-07-23 15:29:45 -04005588 * Scroll the focused text field/area to match the WebTextView
Cary Clarkeaa18de2009-09-28 12:50:42 -04005589 * @param xPercent New x position of the WebTextView from 0 to 1.
Leon Scroggins72543e12009-07-23 15:29:45 -04005590 * @param y New y position of the WebTextView in view coordinates
5591 */
Cary Clarkeaa18de2009-09-28 12:50:42 -04005592 /*package*/ void scrollFocusedTextInput(float xPercent, int y) {
Leon Scroggins72543e12009-07-23 15:29:45 -04005593 if (!inEditingMode() || mWebViewCore == null) {
5594 return;
5595 }
Cary Clarkeaa18de2009-09-28 12:50:42 -04005596 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT,
Leon Scrogginsd3997e52009-09-21 14:15:18 -04005597 // Since this position is relative to the top of the text input
5598 // field, we do not need to take the title bar's height into
5599 // consideration.
Cary Clarkeaa18de2009-09-28 12:50:42 -04005600 viewToContentDimension(y),
5601 new Float(xPercent));
Leon Scroggins72543e12009-07-23 15:29:45 -04005602 }
5603
5604 /**
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005605 * Set our starting point and time for a drag from the WebTextView.
5606 */
5607 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
5608 if (!inEditingMode()) {
5609 return;
5610 }
5611 mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
5612 mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
5613 mLastTouchTime = eventTime;
5614 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04005615 abortAnimation();
Grace Kloba96949ef2010-01-25 09:53:01 -08005616 mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005617 }
5618 mSnapScrollMode = SNAP_NONE;
5619 mVelocityTracker = VelocityTracker.obtain();
5620 mTouchMode = TOUCH_DRAG_START_MODE;
5621 }
5622
5623 /**
5624 * Given a motion event from the WebTextView, set its location to our
5625 * coordinates, and handle the event.
5626 */
5627 /*package*/ boolean textFieldDrag(MotionEvent event) {
5628 if (!inEditingMode()) {
5629 return false;
5630 }
Leon Scroggins72543e12009-07-23 15:29:45 -04005631 mDragFromTextInput = true;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005632 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
5633 (float) (mWebTextView.getTop() - mScrollY));
Leon Scroggins72543e12009-07-23 15:29:45 -04005634 boolean result = onTouchEvent(event);
5635 mDragFromTextInput = false;
5636 return result;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04005637 }
5638
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005639 /**
Leon Scrogginsf90b1262009-11-24 14:49:21 -05005640 * Due a touch up from a WebTextView. This will be handled by webkit to
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005641 * change the selection.
5642 * @param event MotionEvent in the WebTextView's coordinates.
5643 */
5644 /*package*/ void touchUpOnTextField(MotionEvent event) {
5645 if (!inEditingMode()) {
5646 return;
5647 }
Leon Scroggins0236e672009-09-02 21:12:08 -04005648 int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
5649 int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
Leon Scrogginsf90b1262009-11-24 14:49:21 -05005650 nativeMotionUp(x, y, mNavSlop);
Leon Scroggins6679f2f2009-08-12 18:48:10 -04005651 }
5652
Leon Scroggins1d96ca02009-10-23 11:49:03 -04005653 /**
5654 * Called when pressing the center key or trackball on a textfield.
5655 */
5656 /*package*/ void centerKeyPressOnTextField() {
5657 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
5658 nativeCursorNodePointer());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005659 }
5660
5661 private void doShortPress() {
5662 if (mNativeClass == 0) {
5663 return;
5664 }
Grace Klobac2242f22010-03-05 14:00:26 -08005665 if (mPreventDefault == PREVENT_DEFAULT_YES) {
5666 return;
5667 }
5668 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005669 switchOutDrawHistory();
5670 // mLastTouchX and mLastTouchY are the point in the current viewport
Leon Scroggins0236e672009-09-02 21:12:08 -04005671 int contentX = viewToContentX((int) mLastTouchX + mScrollX);
5672 int contentY = viewToContentY((int) mLastTouchY + mScrollY);
Cary Clark1cb97ee2009-12-11 12:10:36 -05005673 if (nativePointInNavCache(contentX, contentY, mNavSlop)) {
5674 WebViewCore.MotionUpData motionUpData = new WebViewCore
5675 .MotionUpData();
5676 motionUpData.mFrame = nativeCacheHitFramePointer();
5677 motionUpData.mNode = nativeCacheHitNodePointer();
5678 motionUpData.mBounds = nativeCacheHitNodeBounds();
5679 motionUpData.mX = contentX;
5680 motionUpData.mY = contentY;
5681 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
5682 motionUpData);
5683 } else {
Cary Clarkbad0c542010-01-11 14:58:21 -05005684 doMotionUp(contentX, contentY);
Cary Clark1cb97ee2009-12-11 12:10:36 -05005685 }
5686 }
5687
Cary Clarkbad0c542010-01-11 14:58:21 -05005688 private void doMotionUp(int contentX, int contentY) {
Dan Egnor18e93962010-02-10 19:27:58 -08005689 if (mLogEvent && nativeMotionUp(contentX, contentY, mNavSlop)) {
5690 EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005691 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005692 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005693 playSoundEffect(SoundEffectConstants.CLICK);
5694 }
5695 }
5696
Grace Klobac6f95fe2010-03-10 13:25:34 -08005697 /*
5698 * Return true if the view (Plugin) is fully visible and maximized inside
5699 * the WebView.
5700 */
5701 private boolean isPluginFitOnScreen(ViewManager.ChildView view) {
5702 int viewWidth = getViewWidth();
5703 int viewHeight = getViewHeightWithTitle();
5704 float scale = Math.min((float) viewWidth / view.width,
5705 (float) viewHeight / view.height);
5706 if (scale < mMinZoomScale) {
5707 scale = mMinZoomScale;
5708 } else if (scale > mMaxZoomScale) {
5709 scale = mMaxZoomScale;
5710 }
5711 if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) {
5712 if (contentToViewX(view.x) >= mScrollX
5713 && contentToViewX(view.x + view.width) <= mScrollX
5714 + viewWidth
5715 && contentToViewY(view.y) >= mScrollY
5716 && contentToViewY(view.y + view.height) <= mScrollY
5717 + viewHeight) {
5718 return true;
5719 }
5720 }
5721 return false;
5722 }
5723
5724 /*
5725 * Maximize and center the view inside the WebView. If the zoom doesn't need
5726 * to be changed, do an animated scroll to center it. If the zoom needs to
5727 * be changed, find the zoom center and do a smooth zoom transition.
5728 */
5729 private void centerPluginOnScreen(ViewManager.ChildView child) {
5730 int viewWidth = getViewWidth();
5731 int viewHeight = getViewHeightWithTitle();
5732 float scale = Math.min((float) viewWidth / child.width,
5733 (float) viewHeight / child.height);
5734 if (scale < mMinZoomScale) {
5735 scale = mMinZoomScale;
5736 } else if (scale > mMaxZoomScale) {
5737 scale = mMaxZoomScale;
5738 }
5739 if (Math.abs(scale - mActualScale) < MINIMUM_SCALE_INCREMENT) {
5740 pinScrollTo(
5741 contentToViewX(child.x + child.width / 2) - viewWidth / 2,
5742 contentToViewY(child.y + child.height / 2) - viewHeight / 2,
5743 true, 0);
5744 } else {
5745 int oldScreenX = contentToViewX(child.x) - mScrollX;
5746 int newPluginX = (int) (child.x * scale);
5747 int newPluginWidth = (int) (child.width * scale);
5748 int newMaxWidth = (int) (mContentWidth * scale);
5749 int newScreenX = (viewWidth - newPluginWidth) / 2;
5750 // pin the newX to the WebView
5751 if (newScreenX > newPluginX) {
5752 newScreenX = newPluginX;
5753 } else if (newScreenX > (newMaxWidth - newPluginX - newPluginWidth)) {
5754 newScreenX = viewWidth - (newMaxWidth - newPluginX);
5755 }
5756 mZoomCenterX = (oldScreenX * scale - newScreenX * mActualScale)
5757 / (scale - mActualScale);
5758 int oldScreenY = contentToViewY(child.y) - mScrollY;
5759 int newPluginY = (int) (child.y * scale) + getTitleHeight();
5760 int newPluginHeight = (int) (child.height * scale);
5761 int newMaxHeight = (int) (mContentHeight * scale) + getTitleHeight();
5762 int newScreenY = (viewHeight - newPluginHeight) / 2;
5763 // pin the newY to the WebView
5764 if (newScreenY > newPluginY) {
5765 newScreenY = newPluginY;
5766 } else if (newScreenY > (newMaxHeight - newPluginY - newPluginHeight)) {
5767 newScreenY = viewHeight - (newMaxHeight - newPluginY);
5768 }
5769 mZoomCenterY = (oldScreenY * scale - newScreenY * mActualScale)
5770 / (scale - mActualScale);
5771 zoomWithPreview(scale, false);
5772 }
5773 }
5774
Grace Kloba3a0def22010-01-23 21:11:54 -08005775 // Rule for double tap:
5776 // 1. if the current scale is not same as the text wrap scale and layout
5777 // algorithm is NARROW_COLUMNS, fit to column;
5778 // 2. if the current state is not overview mode, change to overview mode;
5779 // 3. if the current state is overview mode, change to default scale.
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005780 private void doDoubleTap() {
Leon Scroggins0236e672009-09-02 21:12:08 -04005781 if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005782 return;
5783 }
5784 mZoomCenterX = mLastTouchX;
5785 mZoomCenterY = mLastTouchY;
Grace Kloba3a0def22010-01-23 21:11:54 -08005786 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
5787 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
Grace Klobaf8d8b462009-09-20 15:57:49 -07005788 WebSettings settings = getSettings();
Grace Kloba3a0def22010-01-23 21:11:54 -08005789 // remove the zoom control after double tap
Grace Klobaf8d8b462009-09-20 15:57:49 -07005790 if (settings.getBuiltInZoomControls()) {
Grace Klobad7660cc2009-08-17 17:13:01 -07005791 if (mZoomButtonsController.isVisible()) {
5792 mZoomButtonsController.setVisible(false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005793 }
Grace Klobad7660cc2009-08-17 17:13:01 -07005794 } else {
5795 if (mZoomControlRunnable != null) {
5796 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
5797 }
5798 if (mZoomControls != null) {
5799 mZoomControls.hide();
5800 }
5801 }
Grace Klobaf8d8b462009-09-20 15:57:49 -07005802 settings.setDoubleTapToastCount(0);
Grace Klobac6f95fe2010-03-10 13:25:34 -08005803 ViewManager.ChildView plugin = mViewManager.hitTest(mAnchorX, mAnchorY);
5804 if (plugin != null) {
5805 if (isPluginFitOnScreen(plugin)) {
5806 mInZoomOverview = true;
5807 // Force the titlebar fully reveal in overview mode
5808 if (mScrollY < getTitleHeight()) mScrollY = 0;
5809 zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth,
5810 true);
5811 } else {
5812 mInZoomOverview = false;
5813 centerPluginOnScreen(plugin);
5814 }
5815 return;
5816 }
Grace Kloba3a0def22010-01-23 21:11:54 -08005817 boolean zoomToDefault = false;
5818 if ((settings.getLayoutAlgorithm() == WebSettings.LayoutAlgorithm.NARROW_COLUMNS)
Grace Klobac6f95fe2010-03-10 13:25:34 -08005819 && (Math.abs(mActualScale - mTextWrapScale) >= MINIMUM_SCALE_INCREMENT)) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005820 setNewZoomScale(mActualScale, true, true);
5821 float overviewScale = (float) getViewWidth() / mZoomOverviewWidth;
Grace Klobac6f95fe2010-03-10 13:25:34 -08005822 if (Math.abs(mActualScale - overviewScale) < MINIMUM_SCALE_INCREMENT) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005823 mInZoomOverview = true;
5824 }
5825 } else if (!mInZoomOverview) {
Grace Kloba86773fb2009-11-19 11:25:20 -08005826 float newScale = (float) getViewWidth() / mZoomOverviewWidth;
Grace Klobac6f95fe2010-03-10 13:25:34 -08005827 if (Math.abs(mActualScale - newScale) >= MINIMUM_SCALE_INCREMENT) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005828 mInZoomOverview = true;
Grace Kloba86773fb2009-11-19 11:25:20 -08005829 // Force the titlebar fully reveal in overview mode
5830 if (mScrollY < getTitleHeight()) mScrollY = 0;
Grace Klobac6f95fe2010-03-10 13:25:34 -08005831 zoomWithPreview(newScale, true);
5832 } else if (Math.abs(mActualScale - mDefaultScale) >= MINIMUM_SCALE_INCREMENT) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005833 zoomToDefault = true;
Grace Kloba86773fb2009-11-19 11:25:20 -08005834 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005835 } else {
Grace Kloba3a0def22010-01-23 21:11:54 -08005836 zoomToDefault = true;
5837 }
5838 if (zoomToDefault) {
5839 mInZoomOverview = false;
5840 int left = nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005841 if (left != NO_LEFTEDGE) {
Grace Kloba3a0def22010-01-23 21:11:54 -08005842 // add a 5pt padding to the left edge.
5843 int viewLeft = contentToViewX(left < 5 ? 0 : (left - 5))
5844 - mScrollX;
5845 // Re-calculate the zoom center so that the new scroll x will be
5846 // on the left edge.
5847 if (viewLeft > 0) {
5848 mZoomCenterX = viewLeft * mDefaultScale
5849 / (mDefaultScale - mActualScale);
5850 } else {
5851 scrollBy(viewLeft, 0);
5852 mZoomCenterX = 0;
5853 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005854 }
Grace Klobac6f95fe2010-03-10 13:25:34 -08005855 zoomWithPreview(mDefaultScale, true);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005856 }
5857 }
5858
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005859 // Called by JNI to handle a touch on a node representing an email address,
5860 // address, or phone number
5861 private void overrideLoading(String url) {
5862 mCallbackProxy.uiOverrideUrlLoading(url);
5863 }
5864
5865 @Override
5866 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
5867 boolean result = false;
5868 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04005869 result = mWebTextView.requestFocus(direction,
5870 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005871 } else {
5872 result = super.requestFocus(direction, previouslyFocusedRect);
5873 if (mWebViewCore.getSettings().getNeedInitialFocus()) {
5874 // For cases such as GMail, where we gain focus from a direction,
5875 // we want to move to the first available link.
5876 // FIXME: If there are no visible links, we may not want to
5877 int fakeKeyDirection = 0;
5878 switch(direction) {
5879 case View.FOCUS_UP:
5880 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
5881 break;
5882 case View.FOCUS_DOWN:
5883 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
5884 break;
5885 case View.FOCUS_LEFT:
5886 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
5887 break;
5888 case View.FOCUS_RIGHT:
5889 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
5890 break;
5891 default:
5892 return result;
5893 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005894 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05005895 navHandledKey(fakeKeyDirection, 1, true, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005896 }
5897 }
5898 }
5899 return result;
5900 }
5901
5902 @Override
5903 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
5904 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
5905
5906 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
5907 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
5908 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
5909 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
5910
5911 int measuredHeight = heightSize;
5912 int measuredWidth = widthSize;
5913
5914 // Grab the content size from WebViewCore.
Grace Klobae621d6f2009-09-11 13:20:39 -07005915 int contentHeight = contentToViewDimension(mContentHeight);
5916 int contentWidth = contentToViewDimension(mContentWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005917
5918// Log.d(LOGTAG, "------- measure " + heightMode);
5919
5920 if (heightMode != MeasureSpec.EXACTLY) {
5921 mHeightCanMeasure = true;
5922 measuredHeight = contentHeight;
5923 if (heightMode == MeasureSpec.AT_MOST) {
5924 // If we are larger than the AT_MOST height, then our height can
5925 // no longer be measured and we should scroll internally.
5926 if (measuredHeight > heightSize) {
5927 measuredHeight = heightSize;
5928 mHeightCanMeasure = false;
5929 }
5930 }
5931 } else {
5932 mHeightCanMeasure = false;
5933 }
5934 if (mNativeClass != 0) {
5935 nativeSetHeightCanMeasure(mHeightCanMeasure);
5936 }
5937 // For the width, always use the given size unless unspecified.
5938 if (widthMode == MeasureSpec.UNSPECIFIED) {
5939 mWidthCanMeasure = true;
5940 measuredWidth = contentWidth;
5941 } else {
5942 mWidthCanMeasure = false;
5943 }
5944
5945 synchronized (this) {
5946 setMeasuredDimension(measuredWidth, measuredHeight);
5947 }
5948 }
5949
5950 @Override
5951 public boolean requestChildRectangleOnScreen(View child,
5952 Rect rect,
5953 boolean immediate) {
5954 rect.offset(child.getLeft() - child.getScrollX(),
5955 child.getTop() - child.getScrollY());
5956
Cary Clark31b83672010-03-09 09:20:34 -05005957 Rect content = new Rect(viewToContentX(mScrollX),
5958 viewToContentY(mScrollY),
5959 viewToContentX(mScrollX + getWidth()
5960 - getVerticalScrollbarWidth()),
5961 viewToContentY(mScrollY + getViewHeightWithTitle()));
5962 content = nativeSubtractLayers(content);
5963 int screenTop = contentToViewY(content.top);
5964 int screenBottom = contentToViewY(content.bottom);
5965 int height = screenBottom - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005966 int scrollYDelta = 0;
5967
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005968 if (rect.bottom > screenBottom) {
5969 int oneThirdOfScreenHeight = height / 3;
5970 if (rect.height() > 2 * oneThirdOfScreenHeight) {
5971 // If the rectangle is too tall to fit in the bottom two thirds
5972 // of the screen, place it at the top.
5973 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005974 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005975 // If the rectangle will still fit on screen, we want its
5976 // top to be in the top third of the screen.
5977 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005978 }
5979 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07005980 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005981 }
5982
Cary Clark31b83672010-03-09 09:20:34 -05005983 int screenLeft = contentToViewX(content.left);
5984 int screenRight = contentToViewX(content.right);
5985 int width = screenRight - screenLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005986 int scrollXDelta = 0;
5987
5988 if (rect.right > screenRight && rect.left > screenLeft) {
5989 if (rect.width() > width) {
5990 scrollXDelta += (rect.left - screenLeft);
5991 } else {
5992 scrollXDelta += (rect.right - screenRight);
5993 }
5994 } else if (rect.left < screenLeft) {
5995 scrollXDelta -= (screenLeft - rect.left);
5996 }
5997
5998 if ((scrollYDelta | scrollXDelta) != 0) {
5999 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
6000 }
6001
6002 return false;
6003 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006004
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006005 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
6006 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04006007 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
6008 arg.mReplace = replace;
6009 arg.mNewStart = newStart;
6010 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07006011 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04006012 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006013 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
6014 }
6015
6016 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkded054c2009-06-15 10:26:08 -04006017 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
6018 arg.mEvent = event;
6019 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006020 // Increase our text generation number, and pass it to webcore thread
6021 mTextGeneration++;
6022 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
6023 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04006024 // To make sure the host application, like Browser, has the up to date
6025 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006026 // document state.
6027 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
6028 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04006029 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006030 }
6031
Andrei Popescu04098572010-03-09 12:23:19 +00006032 /* package */ synchronized WebViewCore getWebViewCore() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006033 return mWebViewCore;
6034 }
6035
6036 //-------------------------------------------------------------------------
6037 // Methods can be called from a separate thread, like WebViewCore
6038 // If it needs to call the View system, it has to send message.
6039 //-------------------------------------------------------------------------
6040
6041 /**
6042 * General handler to receive message coming from webkit thread
6043 */
6044 class PrivateHandler extends Handler {
6045 @Override
6046 public void handleMessage(Message msg) {
Cary Clark3e88ddc2009-10-08 14:59:46 -04006047 // exclude INVAL_RECT_MSG_ID since it is frequently output
6048 if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
Grace Klobac2242f22010-03-05 14:00:26 -08006049 if (msg.what >= FIRST_PRIVATE_MSG_ID
6050 && msg.what <= LAST_PRIVATE_MSG_ID) {
6051 Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
6052 - FIRST_PRIVATE_MSG_ID]);
6053 } else if (msg.what >= FIRST_PACKAGE_MSG_ID
6054 && msg.what <= LAST_PACKAGE_MSG_ID) {
6055 Log.v(LOGTAG, HandlerPackageDebugString[msg.what
6056 - FIRST_PACKAGE_MSG_ID]);
6057 } else {
6058 Log.v(LOGTAG, Integer.toString(msg.what));
6059 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006060 }
Grace Kloba207308a2009-09-27 11:44:14 -07006061 if (mWebViewCore == null) {
6062 // after WebView's destroy() is called, skip handling messages.
6063 return;
6064 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006065 switch (msg.what) {
6066 case REMEMBER_PASSWORD: {
6067 mDatabase.setUsernamePassword(
6068 msg.getData().getString("host"),
6069 msg.getData().getString("username"),
6070 msg.getData().getString("password"));
6071 ((Message) msg.obj).sendToTarget();
6072 break;
6073 }
6074 case NEVER_REMEMBER_PASSWORD: {
6075 mDatabase.setUsernamePassword(
6076 msg.getData().getString("host"), null, null);
6077 ((Message) msg.obj).sendToTarget();
6078 break;
6079 }
Grace Klobac2242f22010-03-05 14:00:26 -08006080 case PREVENT_DEFAULT_TIMEOUT: {
6081 // if timeout happens, cancel it so that it won't block UI
6082 // to continue handling touch events
6083 if ((msg.arg1 == MotionEvent.ACTION_DOWN
6084 && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES)
6085 || (msg.arg1 == MotionEvent.ACTION_MOVE
6086 && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN)) {
6087 cancelWebCoreTouchEvent(
6088 viewToContentX((int) mLastTouchX + mScrollX),
6089 viewToContentY((int) mLastTouchY + mScrollY),
6090 true);
Grace Klobaf58af622009-09-24 17:41:23 -07006091 }
Grace Klobac2242f22010-03-05 14:00:26 -08006092 break;
6093 }
6094 case SWITCH_TO_SHORTPRESS: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006095 if (mTouchMode == TOUCH_INIT_MODE) {
Grace Klobac2242f22010-03-05 14:00:26 -08006096 if (mPreventDefault != PREVENT_DEFAULT_YES) {
6097 mTouchMode = TOUCH_SHORTPRESS_START_MODE;
6098 updateSelection();
6099 } else {
6100 // set to TOUCH_SHORTPRESS_MODE so that it won't
6101 // trigger double tap any more
6102 mTouchMode = TOUCH_SHORTPRESS_MODE;
6103 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006104 } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
6105 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006106 }
6107 break;
6108 }
6109 case SWITCH_TO_LONGPRESS: {
Grace Klobac2242f22010-03-05 14:00:26 -08006110 if (inFullScreenMode() || mDeferTouchProcess) {
6111 TouchEventData ted = new TouchEventData();
Grace Kloba5f68d6f2009-12-08 18:42:54 -08006112 ted.mAction = WebViewCore.ACTION_LONGPRESS;
6113 ted.mX = viewToContentX((int) mLastTouchX + mScrollX);
6114 ted.mY = viewToContentY((int) mLastTouchY + mScrollY);
Grace Klobac2242f22010-03-05 14:00:26 -08006115 // metaState for long press is tricky. Should it be the
6116 // state when the press started or when the press was
6117 // released? Or some intermediary key state? For
6118 // simplicity for now, we don't set it.
Ben Murdoch8a032a32010-02-02 18:20:11 +00006119 ted.mMetaState = 0;
Grace Klobac2242f22010-03-05 14:00:26 -08006120 ted.mReprocess = mDeferTouchProcess;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08006121 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Grace Klobac2242f22010-03-05 14:00:26 -08006122 } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
Grace Kloba6c451b72009-06-25 12:25:30 -07006123 mTouchMode = TOUCH_DONE_MODE;
Grace Klobac2242f22010-03-05 14:00:26 -08006124 performLongClick();
6125 rebuildWebTextView();
Grace Kloba6c451b72009-06-25 12:25:30 -07006126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006127 break;
6128 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006129 case RELEASE_SINGLE_TAP: {
Grace Klobac2242f22010-03-05 14:00:26 -08006130 doShortPress();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006131 break;
6132 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006133 case SCROLL_BY_MSG_ID:
6134 setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
6135 break;
6136 case SYNC_SCROLL_TO_MSG_ID:
6137 if (mUserScroll) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006138 // if user has scrolled explicitly, don't sync the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006139 // scroll position any more
6140 mUserScroll = false;
6141 break;
6142 }
6143 // fall through
6144 case SCROLL_TO_MSG_ID:
6145 if (setContentScrollTo(msg.arg1, msg.arg2)) {
6146 // if we can't scroll to the exact position due to pin,
Cary Clarkd6982c92009-05-29 11:02:22 -04006147 // send a message to WebCore to re-scroll when we get a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006148 // new picture
6149 mUserScroll = false;
6150 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
6151 msg.arg1, msg.arg2);
6152 }
6153 break;
6154 case SPAWN_SCROLL_TO_MSG_ID:
6155 spawnContentScrollTo(msg.arg1, msg.arg2);
6156 break;
Grace Kloba769ed212010-01-27 10:52:47 -08006157 case UPDATE_ZOOM_RANGE: {
6158 WebViewCore.RestoreState restoreState
6159 = (WebViewCore.RestoreState) msg.obj;
6160 // mScrollX contains the new minPrefWidth
6161 updateZoomRange(restoreState, getViewWidth(),
6162 restoreState.mScrollX, false);
6163 break;
6164 }
Grace Klobae397a882009-08-06 12:04:14 -07006165 case NEW_PICTURE_MSG_ID: {
6166 WebSettings settings = mWebViewCore.getSettings();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006167 // called for new content
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006168 final int viewWidth = getViewWidth();
Cary Clarkd6982c92009-05-29 11:02:22 -04006169 final WebViewCore.DrawData draw =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006170 (WebViewCore.DrawData) msg.obj;
6171 final Point viewSize = draw.mViewPoint;
Grace Klobae397a882009-08-06 12:04:14 -07006172 boolean useWideViewport = settings.getUseWideViewPort();
Grace Klobaef347ef2009-07-30 11:20:32 -07006173 WebViewCore.RestoreState restoreState = draw.mRestoreState;
Grace Kloba9a67c822009-12-20 11:33:58 -08006174 boolean hasRestoreState = restoreState != null;
6175 if (hasRestoreState) {
Grace Kloba769ed212010-01-27 10:52:47 -08006176 updateZoomRange(restoreState, viewSize.x,
6177 draw.mMinPrefWidth, true);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006178 if (!mDrawHistory) {
6179 mInZoomOverview = false;
6180
6181 if (mInitialScaleInPercent > 0) {
6182 setNewZoomScale(mInitialScaleInPercent / 100.0f,
Grace Kloba3a0def22010-01-23 21:11:54 -08006183 mInitialScaleInPercent != mTextWrapScale * 100,
6184 false);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006185 } else if (restoreState.mViewScale > 0) {
6186 mTextWrapScale = restoreState.mTextWrapScale;
6187 setNewZoomScale(restoreState.mViewScale, false,
Grace Kloba3a0def22010-01-23 21:11:54 -08006188 false);
Grace Kloba3a0def22010-01-23 21:11:54 -08006189 } else {
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006190 mInZoomOverview = useWideViewport
6191 && settings.getLoadWithOverviewMode();
6192 float scale;
6193 if (mInZoomOverview) {
6194 scale = (float) viewWidth
6195 / DEFAULT_VIEWPORT_WIDTH;
6196 } else {
6197 scale = restoreState.mTextWrapScale;
6198 }
6199 setNewZoomScale(scale, Math.abs(scale
Grace Klobac6f95fe2010-03-10 13:25:34 -08006200 - mTextWrapScale) >= MINIMUM_SCALE_INCREMENT,
6201 false);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006202 }
6203 setContentScrollTo(restoreState.mScrollX,
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04006204 restoreState.mScrollY);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08006205 // As we are on a new page, remove the WebTextView. This
6206 // is necessary for page loads driven by webkit, and in
6207 // particular when the user was on a password field, so
6208 // the WebTextView was visible.
6209 clearTextEntry(false);
6210 // update the zoom buttons as the scale can be changed
6211 if (getSettings().getBuiltInZoomControls()) {
6212 updateZoomButtonsEnabled();
6213 }
Mike Reed8b302092009-11-12 12:50:20 -05006214 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006215 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006216 // We update the layout (i.e. request a layout from the
6217 // view system) if the last view size that we sent to
6218 // WebCore matches the view size of the picture we just
6219 // received in the fixed dimension.
6220 final boolean updateLayout = viewSize.x == mLastWidthSent
6221 && viewSize.y == mLastHeightSent;
Cary Clarkd6982c92009-05-29 11:02:22 -04006222 recordNewContentSize(draw.mWidthHeight.x,
Cary Clark5bb6b522009-09-21 11:58:31 -04006223 draw.mWidthHeight.y
6224 + (mFindIsUp ? mFindHeight : 0), updateLayout);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006225 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006226 Rect b = draw.mInvalRegion.getBounds();
6227 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
6228 b.left+","+b.top+","+b.right+","+b.bottom+"}");
6229 }
Mike Reede9e86b82009-09-15 11:26:53 -04006230 invalidateContentRect(draw.mInvalRegion.getBounds());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006231 if (mPictureListener != null) {
6232 mPictureListener.onNewPicture(WebView.this, capturePicture());
6233 }
Grace Klobaef347ef2009-07-30 11:20:32 -07006234 if (useWideViewport) {
Grace Kloba3a0def22010-01-23 21:11:54 -08006235 // limit mZoomOverviewWidth upper bound to
6236 // sMaxViewportWidth so that if the page doesn't behave
6237 // well, the WebView won't go insane. limit the lower
6238 // bound to match the default scale for mobile sites.
Grace Klobaa4fa1072009-11-09 12:01:50 -08006239 mZoomOverviewWidth = Math.min(sMaxViewportWidth, Math
Grace Kloba3a0def22010-01-23 21:11:54 -08006240 .max((int) (viewWidth / mDefaultScale), Math
6241 .max(draw.mMinPrefWidth,
6242 draw.mViewPoint.x)));
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006243 }
6244 if (!mMinZoomScaleFixed) {
Grace Klobae397a882009-08-06 12:04:14 -07006245 mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006246 }
6247 if (!mDrawHistory && mInZoomOverview) {
6248 // fit the content width to the current view. Ignore
6249 // the rounding error case.
6250 if (Math.abs((viewWidth * mInvActualScale)
6251 - mZoomOverviewWidth) > 1) {
Grace Klobae397a882009-08-06 12:04:14 -07006252 setNewZoomScale((float) viewWidth
Grace Kloba3a0def22010-01-23 21:11:54 -08006253 / mZoomOverviewWidth, Math.abs(mActualScale
Grace Klobac6f95fe2010-03-10 13:25:34 -08006254 - mTextWrapScale) < MINIMUM_SCALE_INCREMENT,
6255 false);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07006256 }
6257 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04006258 if (draw.mFocusSizeChanged && inEditingMode()) {
6259 mFocusSizeChanged = true;
6260 }
Grace Kloba9a67c822009-12-20 11:33:58 -08006261 if (hasRestoreState) {
6262 mViewManager.postReadyToDrawAll();
6263 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006264 break;
Grace Klobae397a882009-08-06 12:04:14 -07006265 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006266 case WEBCORE_INITIALIZED_MSG_ID:
6267 // nativeCreate sets mNativeClass to a non-zero value
6268 nativeCreate(msg.arg1);
6269 break;
6270 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
6271 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04006272 // and representing the same node as the pointer.
6273 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006274 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006275 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006276 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006277 int start = Selection.getSelectionStart(text);
6278 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006279 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006280 // Restore the selection, which may have been
6281 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006282 Spannable pword =
6283 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006284 Selection.setSelection(pword, start, end);
6285 // If the text entry has created more events, ignore
6286 // this one.
6287 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006288 mWebTextView.setTextAndKeepSelection(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006289 (String) msg.obj);
6290 }
6291 }
6292 break;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006293 case UPDATE_TEXT_SELECTION_MSG_ID:
Leon Scrogginsbd2754402010-02-09 17:51:08 -05006294 // If no textfield was in focus, and the user touched one,
6295 // causing it to send this message, then WebTextView has not
6296 // been set up yet. Rebuild it so it can set its selection.
6297 rebuildWebTextView();
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006298 if (inEditingMode()
6299 && mWebTextView.isSameTextField(msg.arg1)
6300 && msg.arg2 == mTextGeneration) {
6301 WebViewCore.TextSelectionData tData
6302 = (WebViewCore.TextSelectionData) msg.obj;
6303 mWebTextView.setSelectionFromWebKit(tData.mStart,
6304 tData.mEnd);
6305 }
6306 break;
Leon Scroggins3a503392010-01-06 17:04:38 -05006307 case RETURN_LABEL:
6308 if (inEditingMode()
6309 && mWebTextView.isSameTextField(msg.arg1)) {
6310 mWebTextView.setHint((String) msg.obj);
6311 InputMethodManager imm
6312 = InputMethodManager.peekInstance();
6313 // The hint is propagated to the IME in
6314 // onCreateInputConnection. If the IME is already
6315 // active, restart it so that its hint text is updated.
6316 if (imm != null && imm.isActive(mWebTextView)) {
6317 imm.restartInput(mWebTextView);
6318 }
6319 }
6320 break;
Cary Clark215b72c2009-06-26 14:38:43 -04006321 case MOVE_OUT_OF_PLUGIN:
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05006322 navHandledKey(msg.arg1, 1, false, 0);
Cary Clark215b72c2009-06-26 14:38:43 -04006323 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006324 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04006325 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006326 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04006327 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006328 mWebTextView.bringIntoView();
Leon Scroggins4890feb2009-07-02 10:37:10 -04006329 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006330 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006331 break;
Cary Clark243ea062009-06-25 10:49:32 -04006332 case CLEAR_TEXT_ENTRY:
Leon Scroggins6088e832010-02-17 13:17:32 -05006333 clearTextEntry(false);
Cary Clark243ea062009-06-25 10:49:32 -04006334 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006335 case INVAL_RECT_MSG_ID: {
6336 Rect r = (Rect)msg.obj;
6337 if (r == null) {
6338 invalidate();
6339 } else {
6340 // we need to scale r from content into view coords,
6341 // which viewInvalidate() does for us
6342 viewInvalidate(r.left, r.top, r.right, r.bottom);
6343 }
6344 break;
6345 }
Nicolas Roard38863332010-01-04 19:30:55 +00006346 case IMMEDIATE_REPAINT_MSG_ID: {
Nicolas Roard38863332010-01-04 19:30:55 +00006347 invalidate();
6348 break;
6349 }
6350 case SET_ROOT_LAYER_MSG_ID: {
Cary Clark2ec30692010-02-23 10:50:38 -05006351 nativeSetRootLayer(msg.arg1);
Nicolas Roard38863332010-01-04 19:30:55 +00006352 invalidate();
6353 break;
6354 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006355 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04006356 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006357 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006358 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006359 }
6360 break;
Grace Kloba96949ef2010-01-25 09:53:01 -08006361 case RESUME_WEBCORE_PRIORITY:
Grace Klobaa7bc87c2010-01-29 14:56:25 -08006362 WebViewCore.resumePriority();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006363 break;
6364
Leon Scrogginse3225672009-06-03 15:53:13 -04006365 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006366 // as this is shared by keydown and trackballdown, reset all
6367 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04006368 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006369 mTrackballDown = false;
Grace Kloba98e6fcf2010-01-27 15:20:30 -08006370 performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006371 break;
6372
6373 case WEBCORE_NEED_TOUCH_EVENTS:
6374 mForwardTouchEvents = (msg.arg1 != 0);
6375 break;
6376
6377 case PREVENT_TOUCH_ID:
Grace Klobac2242f22010-03-05 14:00:26 -08006378 if (inFullScreenMode()) {
6379 break;
6380 }
6381 if (msg.obj == null) {
6382 if (msg.arg1 == MotionEvent.ACTION_DOWN
6383 && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {
6384 // if prevent default is called from WebCore, UI
6385 // will not handle the rest of the touch events any
6386 // more.
6387 mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES
6388 : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN;
6389 } else if (msg.arg1 == MotionEvent.ACTION_MOVE
6390 && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
6391 // the return for the first ACTION_MOVE will decide
6392 // whether UI will handle touch or not. Currently no
6393 // support for alternating prevent default
6394 mPreventDefault = msg.arg2 == 1 ? PREVENT_DEFAULT_YES
6395 : PREVENT_DEFAULT_NO;
6396 }
6397 } else if (msg.arg2 == 0) {
6398 // prevent default is not called in WebCore, so the
6399 // message needs to be reprocessed in UI
6400 TouchEventData ted = (TouchEventData) msg.obj;
6401 switch (ted.mAction) {
6402 case MotionEvent.ACTION_DOWN:
6403 mLastDeferTouchX = contentToViewX(ted.mX)
6404 - mScrollX;
6405 mLastDeferTouchY = contentToViewY(ted.mY)
6406 - mScrollY;
6407 mDeferTouchMode = TOUCH_INIT_MODE;
6408 break;
6409 case MotionEvent.ACTION_MOVE: {
6410 // no snapping in defer process
6411 int x = contentToViewX(ted.mX) - mScrollX;
6412 int y = contentToViewY(ted.mY) - mScrollY;
6413 if (mDeferTouchMode != TOUCH_DRAG_MODE) {
6414 mDeferTouchMode = TOUCH_DRAG_MODE;
6415 mLastDeferTouchX = x;
6416 mLastDeferTouchY = y;
6417 startDrag();
6418 }
6419 doDrag((int) (mLastDeferTouchX - x),
6420 (int) (mLastDeferTouchY - y));
6421 mLastDeferTouchX = x;
6422 mLastDeferTouchY = y;
6423 break;
Grace Klobaf58af622009-09-24 17:41:23 -07006424 }
Grace Klobac2242f22010-03-05 14:00:26 -08006425 case MotionEvent.ACTION_UP:
6426 case MotionEvent.ACTION_CANCEL:
6427 if (mDeferTouchMode == TOUCH_DRAG_MODE) {
6428 // no fling in defer process
6429 mScroller.springback(mScrollX, mScrollY, 0,
6430 computeMaxScrollX(), 0,
6431 computeMaxScrollY());
6432 invalidate();
6433 WebViewCore.resumePriority();
6434 }
6435 mDeferTouchMode = TOUCH_DONE_MODE;
6436 break;
6437 case WebViewCore.ACTION_DOUBLETAP:
6438 // doDoubleTap() needs mLastTouchX/Y as anchor
6439 mLastTouchX = contentToViewX(ted.mX) - mScrollX;
6440 mLastTouchY = contentToViewY(ted.mY) - mScrollY;
6441 doDoubleTap();
6442 mDeferTouchMode = TOUCH_DONE_MODE;
6443 break;
6444 case WebViewCore.ACTION_LONGPRESS:
6445 HitTestResult hitTest = getHitTestResult();
6446 if (hitTest != null && hitTest.mType
6447 != HitTestResult.UNKNOWN_TYPE) {
6448 performLongClick();
6449 rebuildWebTextView();
6450 }
6451 mDeferTouchMode = TOUCH_DONE_MODE;
6452 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006453 }
6454 }
6455 break;
6456
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04006457 case REQUEST_KEYBOARD:
6458 if (msg.arg1 == 0) {
6459 hideSoftKeyboard();
6460 } else {
Leon Scroggins04e0a102010-01-08 16:19:27 -05006461 displaySoftKeyboard(1 == msg.arg2);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04006462 }
6463 break;
6464
Leon Scroggins5de63892009-10-29 09:48:43 -04006465 case FIND_AGAIN:
6466 // Ignore if find has been dismissed.
6467 if (mFindIsUp) {
6468 findAll(mLastFind);
6469 }
6470 break;
6471
Cary Clark25415e22009-10-12 13:41:28 -04006472 case DRAG_HELD_MOTIONLESS:
6473 mHeldMotionless = MOTIONLESS_TRUE;
6474 invalidate();
6475 // fall through to keep scrollbars awake
6476
6477 case AWAKEN_SCROLL_BARS:
6478 if (mTouchMode == TOUCH_DRAG_MODE
6479 && mHeldMotionless == MOTIONLESS_TRUE) {
6480 awakenScrollBars(ViewConfiguration
6481 .getScrollDefaultDelay(), false);
6482 mPrivateHandler.sendMessageDelayed(mPrivateHandler
6483 .obtainMessage(AWAKEN_SCROLL_BARS),
6484 ViewConfiguration.getScrollDefaultDelay());
6485 }
6486 break;
Cary Clark1cb97ee2009-12-11 12:10:36 -05006487
6488 case DO_MOTION_UP:
Cary Clarkbad0c542010-01-11 14:58:21 -05006489 doMotionUp(msg.arg1, msg.arg2);
Cary Clark1cb97ee2009-12-11 12:10:36 -05006490 break;
6491
Grace Kloba3a0def22010-01-23 21:11:54 -08006492 case SHOW_FULLSCREEN: {
Grace Kloba11438c32009-12-16 11:39:12 -08006493 WebViewCore.PluginFullScreenData data
6494 = (WebViewCore.PluginFullScreenData) msg.obj;
6495 if (data.mNpp != 0 && data.mView != null) {
Grace Klobac2242f22010-03-05 14:00:26 -08006496 if (inFullScreenMode()) {
Grace Kloba11438c32009-12-16 11:39:12 -08006497 Log.w(LOGTAG,
6498 "Should not have another full screen.");
6499 mFullScreenHolder.dismiss();
6500 }
6501 mFullScreenHolder = new PluginFullScreenHolder(
6502 WebView.this, data.mNpp);
Grace Kloba77dc1792010-01-21 19:13:34 -08006503 // as we are sharing the View between full screen and
6504 // embedded mode, we have to remove the
6505 // AbsoluteLayout.LayoutParams set by embedded mode to
6506 // ViewGroup.LayoutParams before adding it to the dialog
Cary Clark28096312010-03-10 16:52:16 -05006507 data.mView.setLayoutParams(new FrameLayout.LayoutParams(
Grace Kloba77dc1792010-01-21 19:13:34 -08006508 ViewGroup.LayoutParams.FILL_PARENT,
6509 ViewGroup.LayoutParams.FILL_PARENT));
Grace Kloba11438c32009-12-16 11:39:12 -08006510 mFullScreenHolder.setContentView(data.mView);
6511 mFullScreenHolder.setCancelable(false);
6512 mFullScreenHolder.setCanceledOnTouchOutside(false);
Grace Kloba4350e142009-12-21 16:35:04 -08006513 mFullScreenHolder.show();
Grace Klobac2242f22010-03-05 14:00:26 -08006514 } else if (!inFullScreenMode()) {
Grace Klobacaf0ce32010-01-25 18:36:48 -08006515 // this may happen if user dismisses the fullscreen and
6516 // then the WebCore re-position message finally reached
6517 // the UI thread.
6518 break;
Grace Kloba11438c32009-12-16 11:39:12 -08006519 }
Grace Kloba4350e142009-12-21 16:35:04 -08006520 // move the matching embedded view fully into the view so
6521 // that touch will be valid instead of rejected due to out
6522 // of the visible bounds
6523 // TODO: do we need to preserve the original position and
6524 // scale so that we can revert it when leaving the full
6525 // screen mode?
6526 int x = contentToViewX(data.mDocX);
6527 int y = contentToViewY(data.mDocY);
6528 int width = contentToViewDimension(data.mDocWidth);
6529 int height = contentToViewDimension(data.mDocHeight);
6530 int viewWidth = getViewWidth();
6531 int viewHeight = getViewHeight();
6532 int newX = mScrollX;
6533 int newY = mScrollY;
6534 if (x < mScrollX) {
6535 newX = x + (width > viewWidth
6536 ? (width - viewWidth) / 2 : 0);
6537 } else if (x + width > mScrollX + viewWidth) {
6538 newX = x + width - viewWidth - (width > viewWidth
6539 ? (width - viewWidth) / 2 : 0);
6540 }
6541 if (y < mScrollY) {
6542 newY = y + (height > viewHeight
6543 ? (height - viewHeight) / 2 : 0);
6544 } else if (y + height > mScrollY + viewHeight) {
6545 newY = y + height - viewHeight - (height > viewHeight
6546 ? (height - viewHeight) / 2 : 0);
6547 }
6548 scrollTo(newX, newY);
6549 if (width > viewWidth || height > viewHeight) {
6550 mZoomCenterX = viewWidth * .5f;
6551 mZoomCenterY = viewHeight * .5f;
Grace Kloba3a0def22010-01-23 21:11:54 -08006552 // do not change text wrap scale so that there is no
6553 // reflow
Grace Kloba4350e142009-12-21 16:35:04 -08006554 setNewZoomScale(mActualScale
6555 / Math.max((float) width / viewWidth,
Grace Kloba3a0def22010-01-23 21:11:54 -08006556 (float) height / viewHeight), false,
6557 false);
Grace Kloba4350e142009-12-21 16:35:04 -08006558 }
6559 // Now update the bound
Grace Kloba11438c32009-12-16 11:39:12 -08006560 mFullScreenHolder.updateBound(contentToViewX(data.mDocX)
6561 - mScrollX, contentToViewY(data.mDocY) - mScrollY,
6562 contentToViewDimension(data.mDocWidth),
6563 contentToViewDimension(data.mDocHeight));
Grace Kloba3a0def22010-01-23 21:11:54 -08006564 }
Grace Kloba11438c32009-12-16 11:39:12 -08006565 break;
6566
6567 case HIDE_FULLSCREEN:
Grace Klobac2242f22010-03-05 14:00:26 -08006568 if (inFullScreenMode()) {
Grace Kloba11438c32009-12-16 11:39:12 -08006569 mFullScreenHolder.dismiss();
6570 mFullScreenHolder = null;
6571 }
6572 break;
6573
Leon Scrogginse26efa32009-12-15 16:38:45 -05006574 case DOM_FOCUS_CHANGED:
6575 if (inEditingMode()) {
6576 nativeClearCursor();
6577 rebuildWebTextView();
6578 }
6579 break;
6580
Grace Kloba3a0def22010-01-23 21:11:54 -08006581 case SHOW_RECT_MSG_ID: {
6582 WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
6583 int x = mScrollX;
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08006584 int left = contentToViewX(data.mLeft);
Grace Kloba3a0def22010-01-23 21:11:54 -08006585 int width = contentToViewDimension(data.mWidth);
6586 int maxWidth = contentToViewDimension(data.mContentWidth);
6587 int viewWidth = getViewWidth();
6588 if (width < viewWidth) {
6589 // center align
6590 x += left + width / 2 - mScrollX - viewWidth / 2;
6591 } else {
6592 x += (int) (left + data.mXPercentInDoc * width
6593 - mScrollX - data.mXPercentInView * viewWidth);
6594 }
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08006595 if (DebugFlags.WEB_VIEW) {
6596 Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
6597 width + ",maxWidth=" + maxWidth +
6598 ",viewWidth=" + viewWidth + ",x="
6599 + x + ",xPercentInDoc=" + data.mXPercentInDoc +
6600 ",xPercentInView=" + data.mXPercentInView+ ")");
6601 }
Grace Kloba3a0def22010-01-23 21:11:54 -08006602 // use the passing content width to cap x as the current
6603 // mContentWidth may not be updated yet
6604 x = Math.max(0,
6605 (Math.min(maxWidth, x + viewWidth)) - viewWidth);
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08006606 int top = contentToViewY(data.mTop);
Grace Kloba3a0def22010-01-23 21:11:54 -08006607 int height = contentToViewDimension(data.mHeight);
6608 int maxHeight = contentToViewDimension(data.mContentHeight);
6609 int viewHeight = getViewHeight();
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08006610 int y = (int) (top + data.mYPercentInDoc * height -
6611 data.mYPercentInView * viewHeight);
6612 if (DebugFlags.WEB_VIEW) {
6613 Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
6614 height + ",maxHeight=" + maxHeight +
6615 ",viewHeight=" + viewHeight + ",y="
6616 + y + ",yPercentInDoc=" + data.mYPercentInDoc +
6617 ",yPercentInView=" + data.mYPercentInView+ ")");
Grace Kloba3a0def22010-01-23 21:11:54 -08006618 }
6619 // use the passing content height to cap y as the current
6620 // mContentHeight may not be updated yet
6621 y = Math.max(0,
6622 (Math.min(maxHeight, y + viewHeight) - viewHeight));
Shimeng (Simon) Wangb7f17d42010-02-08 15:17:21 -08006623 // We need to take into account the visible title height
6624 // when scrolling since y is an absolute view position.
6625 y = Math.max(0, y - getVisibleTitleHeight());
Grace Kloba3a0def22010-01-23 21:11:54 -08006626 scrollTo(x, y);
6627 }
6628 break;
6629
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006630 default:
6631 super.handleMessage(msg);
6632 break;
6633 }
6634 }
6635 }
6636
6637 // Class used to use a dropdown for a <select> element
6638 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006639 // Whether the listbox allows multiple selection.
6640 private boolean mMultiple;
6641 // Passed in to a list with multiple selection to tell
6642 // which items are selected.
6643 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04006644 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006645 // where the initial selection is.
6646 private int mSelection;
6647
6648 private Container[] mContainers;
6649
6650 // Need these to provide stable ids to my ArrayAdapter,
6651 // which normally does not have stable ids. (Bug 1250098)
6652 private class Container extends Object {
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006653 /**
6654 * Possible values for mEnabled. Keep in sync with OptionStatus in
6655 * WebViewCore.cpp
6656 */
6657 final static int OPTGROUP = -1;
6658 final static int OPTION_DISABLED = 0;
6659 final static int OPTION_ENABLED = 1;
6660
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006661 String mString;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006662 int mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006663 int mId;
6664
6665 public String toString() {
6666 return mString;
6667 }
6668 }
6669
6670 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04006671 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006672 * and allow filtering.
6673 */
6674 private class MyArrayListAdapter extends ArrayAdapter<Container> {
6675 public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006676 super(context,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006677 multiple ? com.android.internal.R.layout.select_dialog_multichoice :
Cary Clarkd6982c92009-05-29 11:02:22 -04006678 com.android.internal.R.layout.select_dialog_singlechoice,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006679 objects);
6680 }
6681
6682 @Override
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006683 public View getView(int position, View convertView,
6684 ViewGroup parent) {
6685 // Always pass in null so that we will get a new CheckedTextView
6686 // Otherwise, an item which was previously used as an <optgroup>
6687 // element (i.e. has no check), could get used as an <option>
6688 // element, which needs a checkbox/radio, but it would not have
6689 // one.
6690 convertView = super.getView(position, null, parent);
6691 Container c = item(position);
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -04006692 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
6693 // ListView does not draw dividers between disabled and
6694 // enabled elements. Use a LinearLayout to provide dividers
6695 LinearLayout layout = new LinearLayout(mContext);
6696 layout.setOrientation(LinearLayout.VERTICAL);
6697 if (position > 0) {
6698 View dividerTop = new View(mContext);
6699 dividerTop.setBackgroundResource(
6700 android.R.drawable.divider_horizontal_bright);
6701 layout.addView(dividerTop);
6702 }
6703
6704 if (Container.OPTGROUP == c.mEnabled) {
6705 // Currently select_dialog_multichoice and
6706 // select_dialog_singlechoice are CheckedTextViews. If
6707 // that changes, the class cast will no longer be valid.
6708 Assert.assertTrue(
6709 convertView instanceof CheckedTextView);
6710 ((CheckedTextView) convertView).setCheckMarkDrawable(
6711 null);
6712 } else {
6713 // c.mEnabled == Container.OPTION_DISABLED
6714 // Draw the disabled element in a disabled state.
6715 convertView.setEnabled(false);
6716 }
6717
6718 layout.addView(convertView);
6719 if (position < getCount() - 1) {
6720 View dividerBottom = new View(mContext);
6721 dividerBottom.setBackgroundResource(
6722 android.R.drawable.divider_horizontal_bright);
6723 layout.addView(dividerBottom);
6724 }
6725 return layout;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006726 }
6727 return convertView;
6728 }
6729
6730 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006731 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04006732 // AdapterView's onChanged method uses this to determine whether
6733 // to restore the old state. Return false so that the old (out
6734 // of date) state does not replace the new, valid state.
6735 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006736 }
6737
6738 private Container item(int position) {
6739 if (position < 0 || position >= getCount()) {
6740 return null;
6741 }
6742 return (Container) getItem(position);
6743 }
6744
6745 @Override
6746 public long getItemId(int position) {
6747 Container item = item(position);
6748 if (item == null) {
6749 return -1;
6750 }
6751 return item.mId;
6752 }
6753
6754 @Override
6755 public boolean areAllItemsEnabled() {
6756 return false;
6757 }
6758
6759 @Override
6760 public boolean isEnabled(int position) {
6761 Container item = item(position);
6762 if (item == null) {
6763 return false;
6764 }
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006765 return Container.OPTION_ENABLED == item.mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006766 }
6767 }
6768
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006769 private InvokeListBox(String[] array, int[] enabled, int[] selected) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006770 mMultiple = true;
6771 mSelectedArray = selected;
6772
6773 int length = array.length;
6774 mContainers = new Container[length];
6775 for (int i = 0; i < length; i++) {
6776 mContainers[i] = new Container();
6777 mContainers[i].mString = array[i];
6778 mContainers[i].mEnabled = enabled[i];
6779 mContainers[i].mId = i;
6780 }
6781 }
6782
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006783 private InvokeListBox(String[] array, int[] enabled, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006784 mSelection = selection;
6785 mMultiple = false;
6786
6787 int length = array.length;
6788 mContainers = new Container[length];
6789 for (int i = 0; i < length; i++) {
6790 mContainers[i] = new Container();
6791 mContainers[i].mString = array[i];
6792 mContainers[i].mEnabled = enabled[i];
6793 mContainers[i].mId = i;
6794 }
6795 }
6796
Leon Scroggins3667ce42009-05-13 15:58:03 -04006797 /*
6798 * Whenever the data set changes due to filtering, this class ensures
6799 * that the checked item remains checked.
6800 */
6801 private class SingleDataSetObserver extends DataSetObserver {
6802 private long mCheckedId;
6803 private ListView mListView;
6804 private Adapter mAdapter;
6805
6806 /*
6807 * Create a new observer.
6808 * @param id The ID of the item to keep checked.
6809 * @param l ListView for getting and clearing the checked states
6810 * @param a Adapter for getting the IDs
6811 */
6812 public SingleDataSetObserver(long id, ListView l, Adapter a) {
6813 mCheckedId = id;
6814 mListView = l;
6815 mAdapter = a;
6816 }
6817
6818 public void onChanged() {
6819 // The filter may have changed which item is checked. Find the
6820 // item that the ListView thinks is checked.
6821 int position = mListView.getCheckedItemPosition();
6822 long id = mAdapter.getItemId(position);
6823 if (mCheckedId != id) {
6824 // Clear the ListView's idea of the checked item, since
6825 // it is incorrect
6826 mListView.clearChoices();
6827 // Search for mCheckedId. If it is in the filtered list,
6828 // mark it as checked
6829 int count = mAdapter.getCount();
6830 for (int i = 0; i < count; i++) {
6831 if (mAdapter.getItemId(i) == mCheckedId) {
6832 mListView.setItemChecked(i, true);
6833 break;
6834 }
6835 }
6836 }
6837 }
6838
6839 public void onInvalidate() {}
6840 }
6841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006842 public void run() {
6843 final ListView listView = (ListView) LayoutInflater.from(mContext)
6844 .inflate(com.android.internal.R.layout.select_dialog, null);
Cary Clarkd6982c92009-05-29 11:02:22 -04006845 final MyArrayListAdapter adapter = new
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006846 MyArrayListAdapter(mContext, mContainers, mMultiple);
6847 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
6848 .setView(listView).setCancelable(true)
6849 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04006850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006851 if (mMultiple) {
6852 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
6853 public void onClick(DialogInterface dialog, int which) {
6854 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04006855 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006856 adapter.getCount(), 0,
6857 listView.getCheckedItemPositions());
6858 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07006859 b.setNegativeButton(android.R.string.cancel,
6860 new DialogInterface.OnClickListener() {
6861 public void onClick(DialogInterface dialog, int which) {
6862 mWebViewCore.sendMessage(
6863 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
6864 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006865 }
6866 final AlertDialog dialog = b.create();
6867 listView.setAdapter(adapter);
6868 listView.setFocusableInTouchMode(true);
6869 // There is a bug (1250103) where the checks in a ListView with
6870 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04006871 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006872 // filtered. Do not allow filtering on multiple lists until
6873 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04006874
Leon Scroggins3667ce42009-05-13 15:58:03 -04006875 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006876 if (mMultiple) {
6877 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
6878 int length = mSelectedArray.length;
6879 for (int i = 0; i < length; i++) {
6880 listView.setItemChecked(mSelectedArray[i], true);
6881 }
6882 } else {
6883 listView.setOnItemClickListener(new OnItemClickListener() {
6884 public void onItemClick(AdapterView parent, View v,
6885 int position, long id) {
6886 mWebViewCore.sendMessage(
6887 EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
6888 dialog.dismiss();
6889 }
6890 });
6891 if (mSelection != -1) {
6892 listView.setSelection(mSelection);
6893 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
6894 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04006895 DataSetObserver observer = new SingleDataSetObserver(
6896 adapter.getItemId(mSelection), listView, adapter);
6897 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006898 }
6899 }
6900 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
6901 public void onCancel(DialogInterface dialog) {
6902 mWebViewCore.sendMessage(
6903 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
6904 }
6905 });
6906 dialog.show();
6907 }
6908 }
6909
6910 /*
6911 * Request a dropdown menu for a listbox with multiple selection.
6912 *
6913 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006914 * @param enabledArray State for each element in the list. See static
6915 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006916 * @param selectedArray Which positions are initally selected.
6917 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006918 void requestListBox(String[] array, int[] enabledArray, int[]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006919 selectedArray) {
6920 mPrivateHandler.post(
6921 new InvokeListBox(array, enabledArray, selectedArray));
6922 }
6923
Grace Kloba769ed212010-01-27 10:52:47 -08006924 private void updateZoomRange(WebViewCore.RestoreState restoreState,
6925 int viewWidth, int minPrefWidth, boolean updateZoomOverview) {
6926 if (restoreState.mMinScale == 0) {
6927 if (restoreState.mMobileSite) {
6928 if (minPrefWidth > Math.max(0, viewWidth)) {
6929 mMinZoomScale = (float) viewWidth / minPrefWidth;
6930 mMinZoomScaleFixed = false;
6931 if (updateZoomOverview) {
6932 WebSettings settings = getSettings();
6933 mInZoomOverview = settings.getUseWideViewPort() &&
6934 settings.getLoadWithOverviewMode();
6935 }
6936 } else {
6937 mMinZoomScale = restoreState.mDefaultScale;
6938 mMinZoomScaleFixed = true;
6939 }
6940 } else {
6941 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
6942 mMinZoomScaleFixed = false;
6943 }
6944 } else {
6945 mMinZoomScale = restoreState.mMinScale;
6946 mMinZoomScaleFixed = true;
6947 }
6948 if (restoreState.mMaxScale == 0) {
6949 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
6950 } else {
6951 mMaxZoomScale = restoreState.mMaxScale;
6952 }
6953 }
6954
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006955 /*
6956 * Request a dropdown menu for a listbox with single selection or a single
6957 * <select> element.
6958 *
6959 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006960 * @param enabledArray State for each element in the list. See static
6961 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006962 * @param selection Which position is initally selected.
6963 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04006964 void requestListBox(String[] array, int[] enabledArray, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006965 mPrivateHandler.post(
6966 new InvokeListBox(array, enabledArray, selection));
6967 }
6968
6969 // called by JNI
Leon Scroggins47fabbf2009-12-08 16:57:26 -05006970 private void sendMoveFocus(int frame, int node) {
6971 mWebViewCore.sendMessage(EventHub.SET_MOVE_FOCUS,
6972 new WebViewCore.CursorData(frame, node, 0, 0));
6973 }
6974
6975 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04006976 private void sendMoveMouse(int frame, int node, int x, int y) {
6977 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
6978 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006979 }
6980
Leon Scroggins0658e8f2009-06-26 14:09:09 -04006981 /*
6982 * Send a mouse move event to the webcore thread.
6983 *
6984 * @param removeFocus Pass true if the "mouse" cursor is now over a node
6985 * which wants key events, but it is not the focus. This
6986 * will make the visual appear as though nothing is in
6987 * focus. Remove the WebTextView, if present, and stop
6988 * drawing the blinking caret.
6989 * called by JNI
6990 */
6991 private void sendMoveMouseIfLatest(boolean removeFocus) {
6992 if (removeFocus) {
Leon Scroggins6088e832010-02-17 13:17:32 -05006993 clearTextEntry(true);
Cary Clark19436562009-06-04 16:25:07 -04006994 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04006995 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
6996 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006997 }
6998
6999 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04007000 private void sendMotionUp(int touchGeneration,
Leon Scroggins6679f2f2009-08-12 18:48:10 -04007001 int frame, int node, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007002 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
7003 touchUpData.mMoveGeneration = touchGeneration;
Cary Clarkd6982c92009-05-29 11:02:22 -04007004 touchUpData.mFrame = frame;
7005 touchUpData.mNode = node;
7006 touchUpData.mX = x;
7007 touchUpData.mY = y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007008 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
7009 }
7010
7011
7012 private int getScaledMaxXScroll() {
7013 int width;
7014 if (mHeightCanMeasure == false) {
7015 width = getViewWidth() / 4;
7016 } else {
7017 Rect visRect = new Rect();
7018 calcOurVisibleRect(visRect);
7019 width = visRect.width() / 2;
7020 }
7021 // FIXME the divisor should be retrieved from somewhere
Leon Scroggins0236e672009-09-02 21:12:08 -04007022 return viewToContentX(width);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007023 }
7024
7025 private int getScaledMaxYScroll() {
7026 int height;
7027 if (mHeightCanMeasure == false) {
7028 height = getViewHeight() / 4;
7029 } else {
7030 Rect visRect = new Rect();
7031 calcOurVisibleRect(visRect);
7032 height = visRect.height() / 2;
7033 }
7034 // FIXME the divisor should be retrieved from somewhere
7035 // the closest thing today is hard-coded into ScrollView.java
7036 // (from ScrollView.java, line 363) int maxJump = height/2;
Cary Clarkdf344372009-09-15 12:47:39 -04007037 return Math.round(height * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007038 }
7039
7040 /**
7041 * Called by JNI to invalidate view
7042 */
7043 private void viewInvalidate() {
7044 invalidate();
7045 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007046
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05007047 /**
7048 * Pass the key to the plugin. This assumes that nativeFocusIsPlugin()
7049 * returned true.
7050 */
7051 private void letPluginHandleNavKey(int keyCode, long time, boolean down) {
7052 int keyEventAction;
7053 int eventHubAction;
7054 if (down) {
7055 keyEventAction = KeyEvent.ACTION_DOWN;
7056 eventHubAction = EventHub.KEY_DOWN;
7057 playSoundEffect(keyCodeToSoundsEffect(keyCode));
7058 } else {
7059 keyEventAction = KeyEvent.ACTION_UP;
7060 eventHubAction = EventHub.KEY_UP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007061 }
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05007062 KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
7063 1, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
Cary Clark215b72c2009-06-26 14:38:43 -04007064 | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
7065 | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
7066 , 0, 0, 0);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05007067 mWebViewCore.sendMessage(eventHubAction, event);
7068 }
7069
7070 // return true if the key was handled
7071 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
7072 long time) {
7073 if (mNativeClass == 0) {
7074 return false;
Cary Clark215b72c2009-06-26 14:38:43 -04007075 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007076 mLastCursorTime = time;
7077 mLastCursorBounds = nativeGetCursorRingBounds();
7078 boolean keyHandled
7079 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04007080 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007081 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
7082 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007083 + " handled=" + keyHandled);
7084 }
7085 if (keyHandled == false || mHeightCanMeasure == false) {
7086 return keyHandled;
7087 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007088 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
7089 if (contentCursorRingBounds.isEmpty()) return keyHandled;
Mike Reede9e86b82009-09-15 11:26:53 -04007090 Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007091 Rect visRect = new Rect();
7092 calcOurVisibleRect(visRect);
7093 Rect outset = new Rect(visRect);
7094 int maxXScroll = visRect.width() / 2;
7095 int maxYScroll = visRect.height() / 2;
7096 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007097 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007098 return keyHandled;
7099 }
7100 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007101 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
7102 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007103 if (maxH > 0) {
7104 pinScrollBy(maxH, 0, true, 0);
7105 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007106 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
7107 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007108 if (maxH < 0) {
7109 pinScrollBy(maxH, 0, true, 0);
7110 }
7111 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007112 if (mLastCursorBounds.isEmpty()) return keyHandled;
7113 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
7114 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007115 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007116 if (DebugFlags.WEB_VIEW) {
7117 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
7118 + contentCursorRingBounds);
7119 }
7120 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007121 mUserScroll = true;
7122 return keyHandled;
7123 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007124
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007125 /**
7126 * Set the background color. It's white by default. Pass
7127 * zero to make the view transparent.
7128 * @param color the ARGB color described by Color.java
7129 */
7130 public void setBackgroundColor(int color) {
7131 mBackgroundColor = color;
7132 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
7133 }
7134
7135 public void debugDump() {
7136 nativeDebugDump();
7137 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
7138 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007140 /**
Mike Reedefe2c722009-10-14 09:42:02 -04007141 * Draw the HTML page into the specified canvas. This call ignores any
7142 * view-specific zoom, scroll offset, or other changes. It does not draw
7143 * any view-specific chrome, such as progress or URL bars.
7144 *
7145 * @hide only needs to be accessible to Browser and testing
7146 */
7147 public void drawPage(Canvas canvas) {
7148 mWebViewCore.drawContentPicture(canvas, 0, false, false);
7149 }
7150
7151 /**
Ben Murdochecbc65c2010-01-13 10:54:56 +00007152 * Set the time to wait between passing touches to WebCore. See also the
7153 * TOUCH_SENT_INTERVAL member for further discussion.
7154 *
7155 * @hide This is only used by the DRT test application.
7156 */
7157 public void setTouchInterval(int interval) {
7158 mCurrentTouchInterval = interval;
7159 }
7160
7161 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007162 * Update our cache with updatedText.
7163 * @param updatedText The new text to put in our cache.
7164 */
7165 /* package */ void updateCachedTextfield(String updatedText) {
7166 // Also place our generation number so that when we look at the cache
7167 // we recognize that it is up to date.
7168 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
7169 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007170
Cary Clark1cb97ee2009-12-11 12:10:36 -05007171 private native int nativeCacheHitFramePointer();
7172 private native Rect nativeCacheHitNodeBounds();
7173 private native int nativeCacheHitNodePointer();
Leon Scroggins4890feb2009-07-02 10:37:10 -04007174 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007175 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04007176 private native int nativeCursorFramePointer();
7177 private native Rect nativeCursorNodeBounds();
Cary Clarkaffa5d22010-01-07 12:18:23 -05007178 private native int nativeCursorNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04007179 /* package */ native boolean nativeCursorMatchesFocus();
7180 private native boolean nativeCursorIntersects(Rect visibleRect);
7181 private native boolean nativeCursorIsAnchor();
7182 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04007183 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04007184 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007185 /**
7186 * Returns true if the native cursor node says it wants to handle key events
7187 * (ala plugins). This can only be called if mNativeClass is non-zero!
7188 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04007189 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007190 private native void nativeDebugDump();
7191 private native void nativeDestroy();
Cary Clark2ec30692010-02-23 10:50:38 -05007192 private native boolean nativeEvaluateLayersAnimations();
7193 private native void nativeDrawExtras(Canvas canvas, int extra);
Cary Clarkd6982c92009-05-29 11:02:22 -04007194 private native void nativeDumpDisplayTree(String urlOrNull);
7195 private native int nativeFindAll(String findLower, String findUpper);
7196 private native void nativeFindNext(boolean forward);
Leon Scroggins3a503392010-01-06 17:04:38 -05007197 /* package */ native int nativeFocusCandidateFramePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007198 private native boolean nativeFocusCandidateIsPassword();
7199 private native boolean nativeFocusCandidateIsRtlText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007200 private native boolean nativeFocusCandidateIsTextInput();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05007201 /* package */ native int nativeFocusCandidateMaxLength();
Leon Scroggins0ca70882009-06-26 17:45:29 -04007202 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007203 private native Rect nativeFocusCandidateNodeBounds();
Cary Clarkaffa5d22010-01-07 12:18:23 -05007204 private native int nativeFocusCandidatePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007205 private native String nativeFocusCandidateText();
7206 private native int nativeFocusCandidateTextSize();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05007207 /**
7208 * Returns an integer corresponding to WebView.cpp::type.
7209 * See WebTextView.setType()
7210 */
7211 private native int nativeFocusCandidateType();
Derek Sollenberger718d69f2009-10-19 15:56:43 -04007212 private native boolean nativeFocusIsPlugin();
Leon Scroggins4890feb2009-07-02 10:37:10 -04007213 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04007214 private native Rect nativeGetCursorRingBounds();
Cary Clark57d2c3a2009-12-23 13:57:15 -05007215 private native String nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04007216 private native boolean nativeHasCursorNode();
7217 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04007218 private native void nativeHideCursor();
Cary Clarkd6982c92009-05-29 11:02:22 -04007219 private native String nativeImageURI(int x, int y);
7220 private native void nativeInstrumentReport();
Leon Scroggins1be40982010-03-01 11:20:31 -05007221 /* package */ native boolean nativeMoveCursorToNextTextInput();
Cary Clarkd6982c92009-05-29 11:02:22 -04007222 // return true if the page has been scrolled
7223 private native boolean nativeMotionUp(int x, int y, int slop);
7224 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04007225 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04007226 boolean noScroll);
7227 private native int nativeMoveGeneration();
7228 private native void nativeMoveSelection(int x, int y,
7229 boolean extendSelection);
Cary Clark1cb97ee2009-12-11 12:10:36 -05007230 private native boolean nativePointInNavCache(int x, int y, int slop);
Cary Clarkd6982c92009-05-29 11:02:22 -04007231 // Like many other of our native methods, you must make sure that
7232 // mNativeClass is not null before calling this method.
7233 private native void nativeRecordButtons(boolean focused,
7234 boolean pressed, boolean invalidate);
7235 private native void nativeSelectBestAt(Rect rect);
Cary Clarkde023c12010-03-03 10:05:16 -05007236 private native void nativeSetFindIsEmpty();
7237 private native void nativeSetFindIsUp(boolean isUp);
Cary Clarkd6982c92009-05-29 11:02:22 -04007238 private native void nativeSetFollowedLink(boolean followed);
7239 private native void nativeSetHeightCanMeasure(boolean measure);
Cary Clark855dd9e2010-01-27 17:03:05 -05007240 private native void nativeSetRootLayer(int layer);
Cary Clark2ec30692010-02-23 10:50:38 -05007241 private native void nativeSetSelectionPointer(boolean set,
7242 float scale, int x, int y, boolean extendSelection);
7243 private native void nativeSetSelectionRegion(boolean set);
Cary Clark31b83672010-03-09 09:20:34 -05007244 private native Rect nativeSubtractLayers(Rect content);
Cary Clarkd6982c92009-05-29 11:02:22 -04007245 private native int nativeTextGeneration();
7246 // Never call this version except by updateCachedTextfield(String) -
7247 // we always want to pass in our generation number.
7248 private native void nativeUpdateCachedTextfield(String updatedText,
7249 int generation);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07007250 // return NO_LEFTEDGE means failure.
7251 private static final int NO_LEFTEDGE = -1;
Cary Clark77d98f42009-07-31 09:40:38 -04007252 private native int nativeGetBlockLeftEdge(int x, int y, float scale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007253}