blob: 9ce95d6fde50e1c2e1a738abdd1f45b3eb154219 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
19import android.app.AlertDialog;
20import android.content.Context;
21import android.content.DialogInterface;
22import android.content.Intent;
23import android.content.DialogInterface.OnCancelListener;
Leon Scroggins3667ce42009-05-13 15:58:03 -040024import android.database.DataSetObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.graphics.Bitmap;
26import android.graphics.Canvas;
27import android.graphics.Color;
28import android.graphics.Paint;
29import android.graphics.Path;
30import android.graphics.Picture;
31import android.graphics.Point;
32import android.graphics.Rect;
33import android.graphics.Region;
34import android.net.http.SslCertificate;
35import android.net.Uri;
36import android.os.Bundle;
37import android.os.Handler;
38import android.os.Message;
39import android.os.ServiceManager;
40import android.os.SystemClock;
41import android.provider.Checkin;
42import android.text.IClipboard;
43import android.text.Selection;
44import android.text.Spannable;
45import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.util.EventLog;
47import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.view.Gravity;
49import android.view.KeyEvent;
50import android.view.LayoutInflater;
51import android.view.MotionEvent;
52import android.view.SoundEffectConstants;
53import android.view.VelocityTracker;
54import android.view.View;
55import android.view.ViewConfiguration;
56import android.view.ViewGroup;
57import android.view.ViewParent;
58import android.view.ViewTreeObserver;
The Android Open Source Project10592532009-03-18 17:39:46 -070059import android.view.animation.AlphaAnimation;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.view.inputmethod.InputMethodManager;
Leon Scrogginsd3465f62009-06-02 10:57:54 -040061import android.webkit.WebTextView.AutoCompleteAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.webkit.WebViewCore.EventHub;
63import android.widget.AbsoluteLayout;
Leon Scroggins3667ce42009-05-13 15:58:03 -040064import android.widget.Adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065import android.widget.AdapterView;
66import android.widget.ArrayAdapter;
The Android Open Source Project10592532009-03-18 17:39:46 -070067import android.widget.FrameLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.widget.ImageView;
69import android.widget.ListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070import android.widget.Scroller;
71import android.widget.Toast;
72import android.widget.ZoomButtonsController;
The Android Open Source Project10592532009-03-18 17:39:46 -070073import android.widget.ZoomControls;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074import android.widget.AdapterView.OnItemClickListener;
75
76import java.io.File;
77import java.io.FileInputStream;
78import java.io.FileNotFoundException;
79import java.io.FileOutputStream;
80import java.io.IOException;
81import java.net.URLDecoder;
82import java.util.ArrayList;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083import java.util.List;
84
85/**
Cary Clarkd6982c92009-05-29 11:02:22 -040086 * <p>A View that displays web pages. This class is the basis upon which you
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 * can roll your own web browser or simply display some online content within your Activity.
88 * It uses the WebKit rendering engine to display
89 * web pages and includes methods to navigate forward and backward
90 * through a history, zoom in and out, perform text searches and more.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -070091 * <p>To enable the built-in zoom, set
92 * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
93 * (introduced in API version 3).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 * <p>Note that, in order for your Activity to access the Internet and load web pages
Cary Clarkd6982c92009-05-29 11:02:22 -040095 * in a WebView, you must add the <var>INTERNET</var> permissions to your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096 * Android Manifest file:</p>
97 * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +010098 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 * <p>This must be a child of the <code>&lt;manifest></code> element.</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100100 *
101 * <h3>Basic usage</h3>
102 *
103 * <p>By default, a WebView provides no browser-like widgets, does not
104 * enable JavaScript and errors will be ignored. If your goal is only
105 * to display some HTML as a part of your UI, this is probably fine;
106 * the user won't need to interact with the web page beyond reading
107 * it, and the web page won't need to interact with the user. If you
108 * actually want a fully blown web browser, then you probably want to
109 * invoke the Browser application with your URL rather than show it
110 * with a WebView. See {@link android.content.Intent} for more information.</p>
111 *
112 * <pre class="prettyprint">
113 * WebView webview = new WebView(this);
114 * setContentView(webview);
115 *
116 * // Simplest usage: note that an exception will NOT be thrown
117 * // if there is an error loading this page (see below).
118 * webview.loadUrl("http://slashdot.org/");
119 *
120 * // Of course you can also load from any string:
121 * String summary = "&lt;html>&lt;body>You scored &lt;b>192</b> points.&lt;/body>&lt;/html>";
122 * webview.loadData(summary, "text/html", "utf-8");
123 * // ... although note that there are restrictions on what this HTML can do.
124 * // See the JavaDocs for loadData and loadDataWithBaseUrl for more info.
125 * </pre>
126 *
127 * <p>A WebView has several customization points where you can add your
128 * own behavior. These are:</p>
129 *
130 * <ul>
131 * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
132 * This class is called when something that might impact a
133 * browser UI happens, for instance, progress updates and
134 * JavaScript alerts are sent here.
135 * </li>
136 * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
137 * It will be called when things happen that impact the
138 * rendering of the content, eg, errors or form submissions. You
139 * can also intercept URL loading here.</li>
140 * <li>Via the {@link android.webkit.WebSettings} class, which contains
141 * miscellaneous configuration. </li>
142 * <li>With the {@link android.webkit.WebView#addJavascriptInterface} method.
143 * This lets you bind Java objects into the WebView so they can be
144 * controlled from the web pages JavaScript.</li>
145 * </ul>
146 *
147 * <p>Here's a more complicated example, showing error handling,
148 * settings, and progress notification:</p>
149 *
150 * <pre class="prettyprint">
151 * // Let's display the progress in the activity title bar, like the
152 * // browser app does.
153 * getWindow().requestFeature(Window.FEATURE_PROGRESS);
154 *
155 * webview.getSettings().setJavaScriptEnabled(true);
156 *
157 * final Activity activity = this;
158 * webview.setWebChromeClient(new WebChromeClient() {
159 * public void onProgressChanged(WebView view, int progress) {
160 * // Activities and WebViews measure progress with different scales.
161 * // The progress meter will automatically disappear when we reach 100%
162 * activity.setProgress(progress * 1000);
163 * }
164 * });
165 * webview.setWebViewClient(new WebViewClient() {
166 * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
167 * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
168 * }
169 * });
170 *
171 * webview.loadUrl("http://slashdot.org/");
172 * </pre>
173 *
174 * <h3>Cookie and window management</h3>
175 *
176 * <p>For obvious security reasons, your application has its own
177 * cache, cookie store etc - it does not share the Browser
178 * applications data. Cookies are managed on a separate thread, so
179 * operations like index building don't block the UI
180 * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
181 * if you want to use cookies in your application.
182 * </p>
183 *
184 * <p>By default, requests by the HTML to open new windows are
185 * ignored. This is true whether they be opened by JavaScript or by
186 * the target attribute on a link. You can customize your
187 * WebChromeClient to provide your own behaviour for opening multiple windows,
188 * and render them in whatever manner you want.</p>
189 *
190 * <p>Standard behavior for an Activity is to be destroyed and
191 * recreated when the devices orientation is changed. This will cause
192 * the WebView to reload the current page. If you don't want that, you
193 * can set your Activity to handle the orientation and keyboardHidden
194 * changes, and then just leave the WebView alone. It'll automatically
195 * re-orient itself as appropriate.</p>
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 */
Cary Clarkd6982c92009-05-29 11:02:22 -0400197public class WebView extends AbsoluteLayout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800198 implements ViewTreeObserver.OnGlobalFocusChangeListener,
199 ViewGroup.OnHierarchyChangeListener {
200
201 // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
202 // the screen all-the-time. Good for profiling our drawing code
203 static private final boolean AUTO_REDRAW_HACK = false;
204 // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
205 private boolean mAutoRedraw;
206
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800207 static final String LOGTAG = "webview";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800208
Cary Clarkded054c2009-06-15 10:26:08 -0400209 static class ScaleLimitData {
210 int mMinScale;
211 int mMaxScale;
212 }
213
Leon Scroggins213a31c2009-05-21 16:49:32 -0700214 private static class ExtendedZoomControls extends FrameLayout {
The Android Open Source Project10592532009-03-18 17:39:46 -0700215 public ExtendedZoomControls(Context context, AttributeSet attrs) {
216 super(context, attrs);
217 LayoutInflater inflater = (LayoutInflater)
218 context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
219 inflater.inflate(com.android.internal.R.layout.zoom_magnify, this, true);
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400220 mPlusMinusZoomControls = (ZoomControls) findViewById(
221 com.android.internal.R.id.zoomControls);
The Android Open Source Project10592532009-03-18 17:39:46 -0700222 mZoomMagnify = (ImageView) findViewById(com.android.internal.R.id.zoomMagnify);
223 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400224
The Android Open Source Project10592532009-03-18 17:39:46 -0700225 public void show(boolean showZoom, boolean canZoomOut) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400226 mPlusMinusZoomControls.setVisibility(
227 showZoom ? View.VISIBLE : View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700228 mZoomMagnify.setVisibility(canZoomOut ? View.VISIBLE : View.GONE);
229 fade(View.VISIBLE, 0.0f, 1.0f);
230 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400231
The Android Open Source Project10592532009-03-18 17:39:46 -0700232 public void hide() {
233 fade(View.GONE, 1.0f, 0.0f);
234 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400235
The Android Open Source Project10592532009-03-18 17:39:46 -0700236 private void fade(int visibility, float startAlpha, float endAlpha) {
237 AlphaAnimation anim = new AlphaAnimation(startAlpha, endAlpha);
238 anim.setDuration(500);
239 startAnimation(anim);
240 setVisibility(visibility);
241 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400242
The Android Open Source Project10592532009-03-18 17:39:46 -0700243 public void setIsZoomMagnifyEnabled(boolean isEnabled) {
244 mZoomMagnify.setEnabled(isEnabled);
245 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400246
The Android Open Source Project10592532009-03-18 17:39:46 -0700247 public boolean hasFocus() {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400248 return mPlusMinusZoomControls.hasFocus() || mZoomMagnify.hasFocus();
The Android Open Source Project10592532009-03-18 17:39:46 -0700249 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400250
The Android Open Source Project10592532009-03-18 17:39:46 -0700251 public void setOnZoomInClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400252 mPlusMinusZoomControls.setOnZoomInClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700253 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400254
The Android Open Source Project10592532009-03-18 17:39:46 -0700255 public void setOnZoomOutClickListener(OnClickListener listener) {
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400256 mPlusMinusZoomControls.setOnZoomOutClickListener(listener);
The Android Open Source Project10592532009-03-18 17:39:46 -0700257 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400258
The Android Open Source Project10592532009-03-18 17:39:46 -0700259 public void setOnZoomMagnifyClickListener(OnClickListener listener) {
260 mZoomMagnify.setOnClickListener(listener);
261 }
262
Leon Scrogginsa5645b22009-06-09 15:31:19 -0400263 ZoomControls mPlusMinusZoomControls;
264 ImageView mZoomMagnify;
The Android Open Source Project10592532009-03-18 17:39:46 -0700265 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400266
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267 /**
268 * Transportation object for returning WebView across thread boundaries.
269 */
270 public class WebViewTransport {
271 private WebView mWebview;
272
273 /**
274 * Set the WebView to the transportation object.
275 * @param webview The WebView to transport.
276 */
277 public synchronized void setWebView(WebView webview) {
278 mWebview = webview;
279 }
280
281 /**
282 * Return the WebView object.
283 * @return WebView The transported WebView object.
284 */
285 public synchronized WebView getWebView() {
286 return mWebview;
287 }
288 }
289
290 // A final CallbackProxy shared by WebViewCore and BrowserFrame.
291 private final CallbackProxy mCallbackProxy;
292
293 private final WebViewDatabase mDatabase;
294
295 // SSL certificate for the main top-level page (if secure)
296 private SslCertificate mCertificate;
297
298 // Native WebView pointer that is 0 until the native object has been
299 // created.
300 private int mNativeClass;
301 // This would be final but it needs to be set to null when the WebView is
302 // destroyed.
303 private WebViewCore mWebViewCore;
304 // Handler for dispatching UI messages.
305 /* package */ final Handler mPrivateHandler = new PrivateHandler();
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400306 private WebTextView mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800307 // Used to ignore changes to webkit text that arrives to the UI side after
308 // more key events.
309 private int mTextGeneration;
310
Patrick Scott0a5ce012009-07-02 08:56:10 -0400311 // Used by WebViewCore to create child views.
312 /* package */ final ViewManager mViewManager;
313
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800314 // The list of loaded plugins.
315 private static PluginList sPluginList;
316
317 /**
318 * Position of the last touch event.
319 */
320 private float mLastTouchX;
321 private float mLastTouchY;
322
323 /**
324 * Time of the last touch event.
325 */
326 private long mLastTouchTime;
327
328 /**
329 * Time of the last time sending touch event to WebViewCore
330 */
331 private long mLastSentTouchTime;
332
333 /**
334 * The minimum elapsed time before sending another ACTION_MOVE event to
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700335 * WebViewCore. This really should be tuned for each type of the devices.
336 * For example in Google Map api test case, it takes Dream device at least
337 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
338 * triggering the layout and drawing the picture. While the same process
339 * takes 60+ms on the current high speed device. If we make
340 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
341 * to WebViewCore queue and the real layout and draw events will be pushed
342 * to further, which slows down the refresh rate. Choose 50 to favor the
343 * current high speed devices. For Dream like devices, 100 is a better
344 * choice. Maybe make this in the buildspec later.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800345 */
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700346 private static final int TOUCH_SENT_INTERVAL = 50;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347
348 /**
349 * Helper class to get velocity for fling
350 */
351 VelocityTracker mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700352 private int mMaximumFling;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 /**
355 * Touch mode
356 */
357 private int mTouchMode = TOUCH_DONE_MODE;
358 private static final int TOUCH_INIT_MODE = 1;
359 private static final int TOUCH_DRAG_START_MODE = 2;
360 private static final int TOUCH_DRAG_MODE = 3;
361 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
362 private static final int TOUCH_SHORTPRESS_MODE = 5;
363 private static final int TOUCH_DOUBLECLICK_MODE = 6;
364 private static final int TOUCH_DONE_MODE = 7;
365 private static final int TOUCH_SELECT_MODE = 8;
366 // touch mode values specific to scale+scroll
367 private static final int FIRST_SCROLL_ZOOM = 9;
368 private static final int SCROLL_ZOOM_ANIMATION_IN = 9;
369 private static final int SCROLL_ZOOM_ANIMATION_OUT = 10;
370 private static final int SCROLL_ZOOM_OUT = 11;
371 private static final int LAST_SCROLL_ZOOM = 11;
372 // end of touch mode values specific to scale+scroll
373
374 // Whether to forward the touch events to WebCore
375 private boolean mForwardTouchEvents = false;
376
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 // Whether to prevent drag during touch. The initial value depends on
378 // mForwardTouchEvents. If WebCore wants touch events, we assume it will
379 // take control of touch events unless it says no for touch down event.
380 private boolean mPreventDrag;
381
Cary Clarkd6982c92009-05-29 11:02:22 -0400382 // Whether or not to draw the cursor ring.
383 private boolean mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384
Mike Reedd205d5b2009-05-27 11:02:29 -0400385 // true if onPause has been called (and not onResume)
386 private boolean mIsPaused;
387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 /**
389 * Customizable constant
390 */
391 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
392 private int mTouchSlopSquare;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700393 // pre-computed density adjusted navigation slop
394 private int mNavSlop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800395 // This should be ViewConfiguration.getTapTimeout()
396 // But system time out is 100ms, which is too short for the browser.
397 // In the browser, if it switches out of tap too soon, jump tap won't work.
398 private static final int TAP_TIMEOUT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 // This should be ViewConfiguration.getLongPressTimeout()
400 // But system time out is 500ms, which is too short for the browser.
401 // With a short timeout, it's difficult to treat trigger a short press.
402 private static final int LONG_PRESS_TIMEOUT = 1000;
403 // needed to avoid flinging after a pause of no movement
404 private static final int MIN_FLING_TIME = 250;
The Android Open Source Project10592532009-03-18 17:39:46 -0700405 // The time that the Zoom Controls are visible before fading away
Cary Clarkd6982c92009-05-29 11:02:22 -0400406 private static final long ZOOM_CONTROLS_TIMEOUT =
The Android Open Source Project10592532009-03-18 17:39:46 -0700407 ViewConfiguration.getZoomControlsTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800408 // The amount of content to overlap between two screens when going through
409 // pages with the space bar, in pixels.
410 private static final int PAGE_SCROLL_OVERLAP = 24;
411
412 /**
413 * These prevent calling requestLayout if either dimension is fixed. This
414 * depends on the layout parameters and the measure specs.
415 */
416 boolean mWidthCanMeasure;
417 boolean mHeightCanMeasure;
418
419 // Remember the last dimensions we sent to the native side so we can avoid
420 // sending the same dimensions more than once.
421 int mLastWidthSent;
422 int mLastHeightSent;
423
424 private int mContentWidth; // cache of value from WebViewCore
425 private int mContentHeight; // cache of value from WebViewCore
426
Cary Clarkd6982c92009-05-29 11:02:22 -0400427 // Need to have the separate control for horizontal and vertical scrollbar
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 // style than the View's single scrollbar style
429 private boolean mOverlayHorizontalScrollbar = true;
430 private boolean mOverlayVerticalScrollbar = false;
431
432 // our standard speed. this way small distances will be traversed in less
433 // time than large distances, but we cap the duration, so that very large
434 // distances won't take too long to get there.
435 private static final int STD_SPEED = 480; // pixels per second
436 // time for the longest scroll animation
437 private static final int MAX_DURATION = 750; // milliseconds
438 private Scroller mScroller;
439
440 private boolean mWrapContent;
441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 /**
443 * Private message ids
444 */
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400445 private static final int REMEMBER_PASSWORD = 1;
446 private static final int NEVER_REMEMBER_PASSWORD = 2;
447 private static final int SWITCH_TO_SHORTPRESS = 3;
448 private static final int SWITCH_TO_LONGPRESS = 4;
449 private static final int REQUEST_FORM_DATA = 6;
450 private static final int SWITCH_TO_CLICK = 7;
451 private static final int RESUME_WEBCORE_UPDATE = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800452
453 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400454 static final int SCROLL_TO_MSG_ID = 10;
455 static final int SCROLL_BY_MSG_ID = 11;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400457 static final int SPAWN_SCROLL_TO_MSG_ID = 12;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800458 //! arg1=x, arg2=y
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400459 static final int SYNC_SCROLL_TO_MSG_ID = 13;
460 static final int NEW_PICTURE_MSG_ID = 14;
461 static final int UPDATE_TEXT_ENTRY_MSG_ID = 15;
462 static final int WEBCORE_INITIALIZED_MSG_ID = 16;
463 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 17;
464 static final int DID_FIRST_LAYOUT_MSG_ID = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400465 static final int MOVE_OUT_OF_PLUGIN = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400466 static final int CLEAR_TEXT_ENTRY = 20;
467
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400468 static final int UPDATE_CLIPBOARD = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400469 static final int LONG_PRESS_CENTER = 23;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400470 static final int PREVENT_TOUCH_ID = 24;
471 static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800472 // obj=Rect in doc coordinates
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400473 static final int INVAL_RECT_MSG_ID = 26;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400474 static final int REQUEST_KEYBOARD = 27;
Cary Clarkd6982c92009-05-29 11:02:22 -0400475
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476 static final String[] HandlerDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400477 "REMEMBER_PASSWORD", // = 1;
478 "NEVER_REMEMBER_PASSWORD", // = 2;
479 "SWITCH_TO_SHORTPRESS", // = 3;
480 "SWITCH_TO_LONGPRESS", // = 4;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700481 "5",
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400482 "REQUEST_FORM_DATA", // = 6;
483 "SWITCH_TO_CLICK", // = 7;
484 "RESUME_WEBCORE_UPDATE", // = 8;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800485 "9",
486 "SCROLL_TO_MSG_ID", // = 10;
487 "SCROLL_BY_MSG_ID", // = 11;
488 "SPAWN_SCROLL_TO_MSG_ID", // = 12;
489 "SYNC_SCROLL_TO_MSG_ID", // = 13;
490 "NEW_PICTURE_MSG_ID", // = 14;
491 "UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
492 "WEBCORE_INITIALIZED_MSG_ID", // = 16;
493 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
494 "DID_FIRST_LAYOUT_MSG_ID", // = 18;
Cary Clark215b72c2009-06-26 14:38:43 -0400495 "MOVE_OUT_OF_PLUGIN", // = 19;
Cary Clark243ea062009-06-25 10:49:32 -0400496 "CLEAR_TEXT_ENTRY", // = 20;
Leon Scrogginsfd06bc82009-06-08 13:22:02 -0400497 "21", // = 21;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800498 "UPDATE_CLIPBOARD", // = 22;
Leon Scrogginse3225672009-06-03 15:53:13 -0400499 "LONG_PRESS_CENTER", // = 23;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800500 "PREVENT_TOUCH_ID", // = 24;
501 "WEBCORE_NEED_TOUCH_EVENTS", // = 25;
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -0400502 "INVAL_RECT_MSG_ID", // = 26;
503 "REQUEST_KEYBOARD" // = 27;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800504 };
505
506 // width which view is considered to be fully zoomed out
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700507 static final int ZOOM_OUT_WIDTH = 1008;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800508
Grace Kloba25737912009-06-19 12:42:47 -0700509 // default scale limit. Depending on the display density
510 private static float DEFAULT_MAX_ZOOM_SCALE;
511 private static float DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800512 // scale limit, which can be set through viewport meta tag in the web page
Grace Kloba25737912009-06-19 12:42:47 -0700513 private float mMaxZoomScale;
514 private float mMinZoomScale;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700515 private boolean mMinZoomScaleFixed = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800516
517 // initial scale in percent. 0 means using default.
518 private int mInitialScale = 0;
519
Grace Kloba25737912009-06-19 12:42:47 -0700520 // default scale. Depending on the display density.
521 static int DEFAULT_SCALE_PERCENT;
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700522 private float mDefaultScale;
Grace Kloba25737912009-06-19 12:42:47 -0700523
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 // set to true temporarily while the zoom control is being dragged
525 private boolean mPreviewZoomOnly = false;
526
527 // computed scale and inverse, from mZoomWidth.
Grace Kloba25737912009-06-19 12:42:47 -0700528 private float mActualScale;
529 private float mInvActualScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800530 // if this is non-zero, it is used on drawing rather than mActualScale
531 private float mZoomScale;
532 private float mInvInitialZoomScale;
533 private float mInvFinalZoomScale;
534 private long mZoomStart;
535 private static final int ZOOM_ANIMATION_LENGTH = 500;
536
537 private boolean mUserScroll = false;
538
539 private int mSnapScrollMode = SNAP_NONE;
540 private static final int SNAP_NONE = 1;
541 private static final int SNAP_X = 2;
542 private static final int SNAP_Y = 3;
543 private static final int SNAP_X_LOCK = 4;
544 private static final int SNAP_Y_LOCK = 5;
545 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400546
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800547 // Used to match key downs and key ups
548 private boolean mGotKeyDown;
549
550 /* package */ static boolean mLogEvent = true;
551 private static final int EVENT_LOG_ZOOM_LEVEL_CHANGE = 70101;
552 private static final int EVENT_LOG_DOUBLE_TAP_DURATION = 70102;
553
554 // for event log
555 private long mLastTouchUpTime = 0;
556
557 /**
558 * URI scheme for telephone number
559 */
560 public static final String SCHEME_TEL = "tel:";
561 /**
562 * URI scheme for email address
563 */
564 public static final String SCHEME_MAILTO = "mailto:";
565 /**
566 * URI scheme for map address
567 */
568 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400569
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 private int mBackgroundColor = Color.WHITE;
571
572 // Used to notify listeners of a new picture.
573 private PictureListener mPictureListener;
574 /**
575 * Interface to listen for new pictures as they change.
576 */
577 public interface PictureListener {
578 /**
579 * Notify the listener that the picture has changed.
580 * @param view The WebView that owns the picture.
581 * @param picture The new picture.
582 */
583 public void onNewPicture(WebView view, Picture picture);
584 }
585
Leon Scroggins3246c222009-05-26 09:51:23 -0400586 // FIXME: Want to make this public, but need to change the API file.
587 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 /**
589 * Default HitTestResult, where the target is unknown
590 */
591 public static final int UNKNOWN_TYPE = 0;
592 /**
593 * HitTestResult for hitting a HTML::a tag
594 */
595 public static final int ANCHOR_TYPE = 1;
596 /**
597 * HitTestResult for hitting a phone number
598 */
599 public static final int PHONE_TYPE = 2;
600 /**
601 * HitTestResult for hitting a map address
602 */
603 public static final int GEO_TYPE = 3;
604 /**
605 * HitTestResult for hitting an email address
606 */
607 public static final int EMAIL_TYPE = 4;
608 /**
609 * HitTestResult for hitting an HTML::img tag
610 */
611 public static final int IMAGE_TYPE = 5;
612 /**
613 * HitTestResult for hitting a HTML::a tag which contains HTML::img
614 */
615 public static final int IMAGE_ANCHOR_TYPE = 6;
616 /**
617 * HitTestResult for hitting a HTML::a tag with src=http
618 */
619 public static final int SRC_ANCHOR_TYPE = 7;
620 /**
621 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
622 */
623 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
624 /**
625 * HitTestResult for hitting an edit text area
626 */
627 public static final int EDIT_TEXT_TYPE = 9;
628
629 private int mType;
630 private String mExtra;
631
632 HitTestResult() {
633 mType = UNKNOWN_TYPE;
634 }
635
636 private void setType(int type) {
637 mType = type;
638 }
639
640 private void setExtra(String extra) {
641 mExtra = extra;
642 }
643
644 public int getType() {
645 return mType;
646 }
647
648 public String getExtra() {
649 return mExtra;
650 }
651 }
652
The Android Open Source Project10592532009-03-18 17:39:46 -0700653 // The View containing the zoom controls
654 private ExtendedZoomControls mZoomControls;
655 private Runnable mZoomControlRunnable;
656
Cary Clarkd6982c92009-05-29 11:02:22 -0400657 private ZoomButtonsController mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700659 // These keep track of the center point of the zoom. They are used to
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 // determine the point around which we should zoom.
661 private float mZoomCenterX;
662 private float mZoomCenterY;
663
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700664 private ZoomButtonsController.OnZoomListener mZoomListener =
665 new ZoomButtonsController.OnZoomListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800666
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 public void onVisibilityChanged(boolean visible) {
668 if (visible) {
669 switchOutDrawHistory();
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700670 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 }
672 }
673
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -0700674 public void onZoom(boolean zoomIn) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800675 if (zoomIn) {
676 zoomIn();
677 } else {
678 zoomOut();
679 }
Cary Clarkd6982c92009-05-29 11:02:22 -0400680
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700681 updateZoomButtonsEnabled();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800682 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 };
Cary Clarkd6982c92009-05-29 11:02:22 -0400684
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800685 /**
686 * Construct a new WebView with a Context object.
687 * @param context A Context object used to access application assets.
688 */
689 public WebView(Context context) {
690 this(context, null);
691 }
692
693 /**
694 * Construct a new WebView with layout parameters.
695 * @param context A Context object used to access application assets.
696 * @param attrs An AttributeSet passed to our parent.
697 */
698 public WebView(Context context, AttributeSet attrs) {
699 this(context, attrs, com.android.internal.R.attr.webViewStyle);
700 }
701
702 /**
703 * Construct a new WebView with layout parameters and a default style.
704 * @param context A Context object used to access application assets.
705 * @param attrs An AttributeSet passed to our parent.
706 * @param defStyle The default style resource ID.
707 */
708 public WebView(Context context, AttributeSet attrs, int defStyle) {
709 super(context, attrs, defStyle);
710 init();
711
712 mCallbackProxy = new CallbackProxy(context, this);
713 mWebViewCore = new WebViewCore(context, this, mCallbackProxy);
714 mDatabase = WebViewDatabase.getInstance(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800715 mScroller = new Scroller(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700716
Patrick Scott0a5ce012009-07-02 08:56:10 -0400717 mViewManager = new ViewManager(this);
718
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -0700719 mZoomButtonsController = new ZoomButtonsController(this);
720 mZoomButtonsController.setOnZoomListener(mZoomListener);
Leon Scrogginsaa3f96a2009-06-11 14:46:35 -0400721 // ZoomButtonsController positions the buttons at the bottom, but in
722 // the middle. Change their layout parameters so they appear on the
723 // right.
724 View controls = mZoomButtonsController.getZoomControls();
725 ViewGroup.LayoutParams params = controls.getLayoutParams();
726 if (params instanceof FrameLayout.LayoutParams) {
727 FrameLayout.LayoutParams frameParams = (FrameLayout.LayoutParams)
728 params;
729 frameParams.gravity = Gravity.RIGHT;
730 }
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700731 }
732
733 private void updateZoomButtonsEnabled() {
The Android Open Source Project10592532009-03-18 17:39:46 -0700734 boolean canZoomIn = mActualScale < mMaxZoomScale;
735 boolean canZoomOut = mActualScale > mMinZoomScale;
736 if (!canZoomIn && !canZoomOut) {
737 // Hide the zoom in and out buttons, as well as the fit to page
738 // button, if the page cannot zoom
739 mZoomButtonsController.getZoomControls().setVisibility(View.GONE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700740 } else {
741 // Bring back the hidden zoom controls.
742 mZoomButtonsController.getZoomControls()
743 .setVisibility(View.VISIBLE);
The Android Open Source Project10592532009-03-18 17:39:46 -0700744 // Set each one individually, as a page may be able to zoom in
745 // or out.
746 mZoomButtonsController.setZoomInEnabled(canZoomIn);
747 mZoomButtonsController.setZoomOutEnabled(canZoomOut);
The Android Open Source Project10592532009-03-18 17:39:46 -0700748 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749 }
750
751 private void init() {
752 setWillNotDraw(false);
753 setFocusable(true);
754 setFocusableInTouchMode(true);
755 setClickable(true);
756 setLongClickable(true);
757
Romain Guy4296fc42009-07-06 11:48:52 -0700758 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
759 final int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 mTouchSlopSquare = slop * slop;
761 mMinLockSnapReverseDistance = slop;
Grace Kloba25737912009-06-19 12:42:47 -0700762 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700763 // use one line height, 16 based on our current default font, for how
764 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -0700765 mNavSlop = (int) (16 * density);
766 // density adjusted scale factors
767 DEFAULT_SCALE_PERCENT = (int) (100 * density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700768 mDefaultScale = density;
Grace Kloba25737912009-06-19 12:42:47 -0700769 mActualScale = density;
770 mInvActualScale = 1 / density;
771 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
772 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
773 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
774 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
Romain Guy4296fc42009-07-06 11:48:52 -0700775 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800776 }
777
Grace Kloba0d8b77c2009-06-25 11:20:51 -0700778 /* package */void updateDefaultZoomDensity(int zoomDensity) {
779 final float density = getContext().getResources().getDisplayMetrics().density
780 * 100 / zoomDensity;
781 if (Math.abs(density - mDefaultScale) > 0.01) {
782 float scaleFactor = density / mDefaultScale;
783 // adjust the limits
784 mNavSlop = (int) (16 * density);
785 DEFAULT_SCALE_PERCENT = (int) (100 * density);
786 DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
787 DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
788 mDefaultScale = density;
789 mMaxZoomScale *= scaleFactor;
790 mMinZoomScale *= scaleFactor;
791 setNewZoomScale(mActualScale * scaleFactor, false);
792 }
793 }
794
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 /* package */ boolean onSavePassword(String schemePlusHost, String username,
796 String password, final Message resumeMsg) {
797 boolean rVal = false;
798 if (resumeMsg == null) {
799 // null resumeMsg implies saving password silently
800 mDatabase.setUsernamePassword(schemePlusHost, username, password);
801 } else {
802 final Message remember = mPrivateHandler.obtainMessage(
803 REMEMBER_PASSWORD);
804 remember.getData().putString("host", schemePlusHost);
805 remember.getData().putString("username", username);
806 remember.getData().putString("password", password);
807 remember.obj = resumeMsg;
808
809 final Message neverRemember = mPrivateHandler.obtainMessage(
810 NEVER_REMEMBER_PASSWORD);
811 neverRemember.getData().putString("host", schemePlusHost);
812 neverRemember.getData().putString("username", username);
813 neverRemember.getData().putString("password", password);
814 neverRemember.obj = resumeMsg;
815
816 new AlertDialog.Builder(getContext())
817 .setTitle(com.android.internal.R.string.save_password_label)
818 .setMessage(com.android.internal.R.string.save_password_message)
819 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
820 new DialogInterface.OnClickListener() {
821 public void onClick(DialogInterface dialog, int which) {
822 resumeMsg.sendToTarget();
823 }
824 })
825 .setNeutralButton(com.android.internal.R.string.save_password_remember,
826 new DialogInterface.OnClickListener() {
827 public void onClick(DialogInterface dialog, int which) {
828 remember.sendToTarget();
829 }
830 })
831 .setNegativeButton(com.android.internal.R.string.save_password_never,
832 new DialogInterface.OnClickListener() {
833 public void onClick(DialogInterface dialog, int which) {
834 neverRemember.sendToTarget();
835 }
836 })
837 .setOnCancelListener(new OnCancelListener() {
838 public void onCancel(DialogInterface dialog) {
839 resumeMsg.sendToTarget();
840 }
841 }).show();
842 // Return true so that WebViewCore will pause while the dialog is
843 // up.
844 rVal = true;
845 }
846 return rVal;
847 }
848
849 @Override
850 public void setScrollBarStyle(int style) {
851 if (style == View.SCROLLBARS_INSIDE_INSET
852 || style == View.SCROLLBARS_OUTSIDE_INSET) {
853 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
854 } else {
855 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
856 }
857 super.setScrollBarStyle(style);
858 }
859
860 /**
861 * Specify whether the horizontal scrollbar has overlay style.
862 * @param overlay TRUE if horizontal scrollbar should have overlay style.
863 */
864 public void setHorizontalScrollbarOverlay(boolean overlay) {
865 mOverlayHorizontalScrollbar = overlay;
866 }
867
868 /**
869 * Specify whether the vertical scrollbar has overlay style.
870 * @param overlay TRUE if vertical scrollbar should have overlay style.
871 */
872 public void setVerticalScrollbarOverlay(boolean overlay) {
873 mOverlayVerticalScrollbar = overlay;
874 }
875
876 /**
877 * Return whether horizontal scrollbar has overlay style
878 * @return TRUE if horizontal scrollbar has overlay style.
879 */
880 public boolean overlayHorizontalScrollbar() {
881 return mOverlayHorizontalScrollbar;
882 }
883
884 /**
885 * Return whether vertical scrollbar has overlay style
886 * @return TRUE if vertical scrollbar has overlay style.
887 */
888 public boolean overlayVerticalScrollbar() {
889 return mOverlayVerticalScrollbar;
890 }
891
892 /*
893 * Return the width of the view where the content of WebView should render
894 * to.
895 */
896 private int getViewWidth() {
897 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
898 return getWidth();
899 } else {
900 return getWidth() - getVerticalScrollbarWidth();
901 }
902 }
903
904 /*
905 * Return the height of the view where the content of WebView should render
906 * to.
907 */
908 private int getViewHeight() {
909 if (!isHorizontalScrollBarEnabled() || mOverlayHorizontalScrollbar) {
910 return getHeight();
911 } else {
912 return getHeight() - getHorizontalScrollbarHeight();
913 }
914 }
915
916 /**
917 * @return The SSL certificate for the main top-level page or null if
918 * there is no certificate (the site is not secure).
919 */
920 public SslCertificate getCertificate() {
921 return mCertificate;
922 }
923
924 /**
925 * Sets the SSL certificate for the main top-level page.
926 */
927 public void setCertificate(SslCertificate certificate) {
928 // here, the certificate can be null (if the site is not secure)
929 mCertificate = certificate;
930 }
931
932 //-------------------------------------------------------------------------
933 // Methods called by activity
934 //-------------------------------------------------------------------------
935
936 /**
937 * Save the username and password for a particular host in the WebView's
938 * internal database.
939 * @param host The host that required the credentials.
940 * @param username The username for the given host.
941 * @param password The password for the given host.
942 */
943 public void savePassword(String host, String username, String password) {
944 mDatabase.setUsernamePassword(host, username, password);
945 }
946
947 /**
948 * Set the HTTP authentication credentials for a given host and realm.
949 *
950 * @param host The host for the credentials.
951 * @param realm The realm for the credentials.
952 * @param username The username for the password. If it is null, it means
953 * password can't be saved.
954 * @param password The password
955 */
956 public void setHttpAuthUsernamePassword(String host, String realm,
957 String username, String password) {
958 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
959 }
960
961 /**
962 * Retrieve the HTTP authentication username and password for a given
963 * host & realm pair
964 *
965 * @param host The host for which the credentials apply.
966 * @param realm The realm for which the credentials apply.
967 * @return String[] if found, String[0] is username, which can be null and
968 * String[1] is password. Return null if it can't find anything.
969 */
970 public String[] getHttpAuthUsernamePassword(String host, String realm) {
971 return mDatabase.getHttpAuthUsernamePassword(host, realm);
972 }
973
974 /**
975 * Destroy the internal state of the WebView. This method should be called
976 * after the WebView has been removed from the view system. No other
977 * methods may be called on a WebView after destroy.
978 */
979 public void destroy() {
980 clearTextEntry();
981 if (mWebViewCore != null) {
982 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -0400983 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800984 mCallbackProxy.setWebViewClient(null);
985 mCallbackProxy.setWebChromeClient(null);
986 // Tell WebViewCore to destroy itself
987 WebViewCore webViewCore = mWebViewCore;
988 mWebViewCore = null; // prevent using partial webViewCore
989 webViewCore.destroy();
990 // Remove any pending messages that might not be serviced yet.
991 mPrivateHandler.removeCallbacksAndMessages(null);
992 mCallbackProxy.removeCallbacksAndMessages(null);
993 // Wake up the WebCore thread just in case it is waiting for a
994 // javascript dialog.
995 synchronized (mCallbackProxy) {
996 mCallbackProxy.notify();
997 }
998 }
999 if (mNativeClass != 0) {
1000 nativeDestroy();
1001 mNativeClass = 0;
1002 }
1003 }
1004
1005 /**
1006 * Enables platform notifications of data state and proxy changes.
1007 */
1008 public static void enablePlatformNotifications() {
1009 Network.enablePlatformNotifications();
1010 }
1011
1012 /**
1013 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001014 * from the Activity's onPause() or onStop().
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 */
1016 public static void disablePlatformNotifications() {
1017 Network.disablePlatformNotifications();
1018 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001019
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 /**
Feng Qianb3081372009-06-29 15:55:18 -07001021 * Sets JavaScript engine flags.
1022 *
1023 * @param flags JS engine flags in a String
1024 *
1025 * @hide pending API solidification
1026 */
1027 public void setJsFlags(String flags) {
1028 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1029 }
1030
1031 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 * Inform WebView of the network state. This is used to set
1033 * the javascript property window.navigator.isOnline and
1034 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1035 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 */
1037 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001038 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1039 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001040 }
1041
1042 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001043 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1045 * method no longer stores the display data for this WebView. The previous
1046 * behavior could potentially leak files if {@link #restoreState} was never
1047 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1048 * and restoring the display data.
1049 * @param outState The Bundle to store the WebView state.
1050 * @return The same copy of the back/forward list used to save the state. If
1051 * saveState fails, the returned list will be null.
1052 * @see #savePicture
1053 * @see #restorePicture
1054 */
1055 public WebBackForwardList saveState(Bundle outState) {
1056 if (outState == null) {
1057 return null;
1058 }
1059 // We grab a copy of the back/forward list because a client of WebView
1060 // may have invalidated the history list by calling clearHistory.
1061 WebBackForwardList list = copyBackForwardList();
1062 final int currentIndex = list.getCurrentIndex();
1063 final int size = list.getSize();
1064 // We should fail saving the state if the list is empty or the index is
1065 // not in a valid range.
1066 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1067 return null;
1068 }
1069 outState.putInt("index", currentIndex);
1070 // FIXME: This should just be a byte[][] instead of ArrayList but
1071 // Parcel.java does not have the code to handle multi-dimensional
1072 // arrays.
1073 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1074 for (int i = 0; i < size; i++) {
1075 WebHistoryItem item = list.getItemAtIndex(i);
1076 byte[] data = item.getFlattenedData();
1077 if (data == null) {
1078 // It would be very odd to not have any data for a given history
1079 // item. And we will fail to rebuild the history list without
1080 // flattened data.
1081 return null;
1082 }
1083 history.add(data);
1084 }
1085 outState.putSerializable("history", history);
1086 if (mCertificate != null) {
1087 outState.putBundle("certificate",
1088 SslCertificate.saveState(mCertificate));
1089 }
1090 return list;
1091 }
1092
1093 /**
1094 * Save the current display data to the Bundle given. Used in conjunction
1095 * with {@link #saveState}.
1096 * @param b A Bundle to store the display data.
1097 * @param dest The file to store the serialized picture data. Will be
1098 * overwritten with this WebView's picture data.
1099 * @return True if the picture was successfully saved.
1100 */
1101 public boolean savePicture(Bundle b, File dest) {
1102 if (dest == null || b == null) {
1103 return false;
1104 }
1105 final Picture p = capturePicture();
1106 try {
1107 final FileOutputStream out = new FileOutputStream(dest);
1108 p.writeToStream(out);
1109 out.close();
1110 } catch (FileNotFoundException e){
1111 e.printStackTrace();
1112 } catch (IOException e) {
1113 e.printStackTrace();
1114 } catch (RuntimeException e) {
1115 e.printStackTrace();
1116 }
1117 if (dest.length() > 0) {
1118 b.putInt("scrollX", mScrollX);
1119 b.putInt("scrollY", mScrollY);
1120 b.putFloat("scale", mActualScale);
1121 return true;
1122 }
1123 return false;
1124 }
1125
1126 /**
1127 * Restore the display data that was save in {@link #savePicture}. Used in
1128 * conjunction with {@link #restoreState}.
1129 * @param b A Bundle containing the saved display data.
1130 * @param src The file where the picture data was stored.
1131 * @return True if the picture was successfully restored.
1132 */
1133 public boolean restorePicture(Bundle b, File src) {
1134 if (src == null || b == null) {
1135 return false;
1136 }
1137 if (src.exists()) {
1138 Picture p = null;
1139 try {
1140 final FileInputStream in = new FileInputStream(src);
1141 p = Picture.createFromStream(in);
1142 in.close();
1143 } catch (FileNotFoundException e){
1144 e.printStackTrace();
1145 } catch (RuntimeException e) {
1146 e.printStackTrace();
1147 } catch (IOException e) {
1148 e.printStackTrace();
1149 }
1150 if (p != null) {
1151 int sx = b.getInt("scrollX", 0);
1152 int sy = b.getInt("scrollY", 0);
1153 float scale = b.getFloat("scale", 1.0f);
1154 mDrawHistory = true;
1155 mHistoryPicture = p;
1156 mScrollX = sx;
1157 mScrollY = sy;
1158 mHistoryWidth = Math.round(p.getWidth() * scale);
1159 mHistoryHeight = Math.round(p.getHeight() * scale);
1160 // as getWidth() / getHeight() of the view are not
1161 // available yet, set up mActualScale, so that when
1162 // onSizeChanged() is called, the rest will be set
1163 // correctly
1164 mActualScale = scale;
1165 invalidate();
1166 return true;
1167 }
1168 }
1169 return false;
1170 }
1171
1172 /**
1173 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001174 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1175 * be called to restore the state of the WebView before using the object. If
1176 * it is called after the WebView has had a chance to build state (load
1177 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 * side-effects. Please note that this method no longer restores the
1179 * display data for this WebView. See {@link #savePicture} and {@link
1180 * #restorePicture} for saving and restoring the display data.
1181 * @param inState The incoming Bundle of state.
1182 * @return The restored back/forward list or null if restoreState failed.
1183 * @see #savePicture
1184 * @see #restorePicture
1185 */
1186 public WebBackForwardList restoreState(Bundle inState) {
1187 WebBackForwardList returnList = null;
1188 if (inState == null) {
1189 return returnList;
1190 }
1191 if (inState.containsKey("index") && inState.containsKey("history")) {
1192 mCertificate = SslCertificate.restoreState(
1193 inState.getBundle("certificate"));
1194
1195 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1196 final int index = inState.getInt("index");
1197 // We can't use a clone of the list because we need to modify the
1198 // shared copy, so synchronize instead to prevent concurrent
1199 // modifications.
1200 synchronized (list) {
1201 final List<byte[]> history =
1202 (List<byte[]>) inState.getSerializable("history");
1203 final int size = history.size();
1204 // Check the index bounds so we don't crash in native code while
1205 // restoring the history index.
1206 if (index < 0 || index >= size) {
1207 return null;
1208 }
1209 for (int i = 0; i < size; i++) {
1210 byte[] data = history.remove(0);
1211 if (data == null) {
1212 // If we somehow have null data, we cannot reconstruct
1213 // the item and thus our history list cannot be rebuilt.
1214 return null;
1215 }
1216 WebHistoryItem item = new WebHistoryItem(data);
1217 list.addHistoryItem(item);
1218 }
1219 // Grab the most recent copy to return to the caller.
1220 returnList = copyBackForwardList();
1221 // Update the copy to have the correct index.
1222 returnList.setCurrentIndex(index);
1223 }
1224 // Remove all pending messages because we are restoring previous
1225 // state.
1226 mWebViewCore.removeMessages();
1227 // Send a restore state message.
1228 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1229 }
1230 return returnList;
1231 }
1232
1233 /**
1234 * Load the given url.
1235 * @param url The url of the resource to load.
1236 */
1237 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001238 if (url == null) {
1239 return;
1240 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 switchOutDrawHistory();
1242 mWebViewCore.sendMessage(EventHub.LOAD_URL, url);
1243 clearTextEntry();
1244 }
1245
1246 /**
Grace Kloba57534302009-05-22 18:55:02 -07001247 * Load the url with postData using "POST" method into the WebView. If url
1248 * is not a network url, it will be loaded with {link
1249 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001250 *
Grace Kloba57534302009-05-22 18:55:02 -07001251 * @param url The url of the resource to load.
1252 * @param postData The data will be passed to "POST" request.
Cary Clarkd6982c92009-05-29 11:02:22 -04001253 *
Grace Kloba57534302009-05-22 18:55:02 -07001254 * @hide pending API solidification
1255 */
1256 public void postUrl(String url, byte[] postData) {
1257 if (URLUtil.isNetworkUrl(url)) {
1258 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001259 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1260 arg.mUrl = url;
1261 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001262 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
1263 clearTextEntry();
1264 } else {
1265 loadUrl(url);
1266 }
1267 }
1268
1269 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 * Load the given data into the WebView. This will load the data into
1271 * WebView using the data: scheme. Content loaded through this mechanism
1272 * does not have the ability to load content from the network.
1273 * @param data A String of data in the given encoding.
1274 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1275 * @param encoding The encoding of the data. i.e. utf-8, base64
1276 */
1277 public void loadData(String data, String mimeType, String encoding) {
1278 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1279 }
1280
1281 /**
1282 * Load the given data into the WebView, use the provided URL as the base
1283 * URL for the content. The base URL is the URL that represents the page
1284 * that is loaded through this interface. As such, it is used for the
1285 * history entry and to resolve any relative URLs. The failUrl is used if
1286 * browser fails to load the data provided. If it is empty or null, and the
1287 * load fails, then no history entry is created.
1288 * <p>
1289 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1290 * files through "file:///android_asset/" for the sub resources is more
1291 * restricted. If you provide null or empty string as baseUrl, you won't be
1292 * able to access asset files. If the baseUrl is anything other than
1293 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1294 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001295 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 * @param baseUrl Url to resolve relative paths with, if null defaults to
1297 * "about:blank"
1298 * @param data A String of data in the given encoding.
1299 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1300 * defaults to "text/html"
1301 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
1302 * @param failUrl URL to use if the content fails to load or null.
1303 */
1304 public void loadDataWithBaseURL(String baseUrl, String data,
1305 String mimeType, String encoding, String failUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001306
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1308 loadData(data, mimeType, encoding);
1309 return;
1310 }
1311 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001312 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1313 arg.mBaseUrl = baseUrl;
1314 arg.mData = data;
1315 arg.mMimeType = mimeType;
1316 arg.mEncoding = encoding;
1317 arg.mFailUrl = failUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001318 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
1319 clearTextEntry();
1320 }
1321
1322 /**
1323 * Stop the current load.
1324 */
1325 public void stopLoading() {
1326 // TODO: should we clear all the messages in the queue before sending
1327 // STOP_LOADING?
1328 switchOutDrawHistory();
1329 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1330 }
1331
1332 /**
1333 * Reload the current url.
1334 */
1335 public void reload() {
1336 switchOutDrawHistory();
1337 mWebViewCore.sendMessage(EventHub.RELOAD);
1338 }
1339
1340 /**
1341 * Return true if this WebView has a back history item.
1342 * @return True iff this WebView has a back history item.
1343 */
1344 public boolean canGoBack() {
1345 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1346 synchronized (l) {
1347 if (l.getClearPending()) {
1348 return false;
1349 } else {
1350 return l.getCurrentIndex() > 0;
1351 }
1352 }
1353 }
1354
1355 /**
1356 * Go back in the history of this WebView.
1357 */
1358 public void goBack() {
1359 goBackOrForward(-1);
1360 }
1361
1362 /**
1363 * Return true if this WebView has a forward history item.
1364 * @return True iff this Webview has a forward history item.
1365 */
1366 public boolean canGoForward() {
1367 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1368 synchronized (l) {
1369 if (l.getClearPending()) {
1370 return false;
1371 } else {
1372 return l.getCurrentIndex() < l.getSize() - 1;
1373 }
1374 }
1375 }
1376
1377 /**
1378 * Go forward in the history of this WebView.
1379 */
1380 public void goForward() {
1381 goBackOrForward(1);
1382 }
1383
1384 /**
1385 * Return true if the page can go back or forward the given
1386 * number of steps.
1387 * @param steps The negative or positive number of steps to move the
1388 * history.
1389 */
1390 public boolean canGoBackOrForward(int steps) {
1391 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1392 synchronized (l) {
1393 if (l.getClearPending()) {
1394 return false;
1395 } else {
1396 int newIndex = l.getCurrentIndex() + steps;
1397 return newIndex >= 0 && newIndex < l.getSize();
1398 }
1399 }
1400 }
1401
1402 /**
1403 * Go to the history item that is the number of steps away from
1404 * the current item. Steps is negative if backward and positive
1405 * if forward.
1406 * @param steps The number of steps to take back or forward in the back
1407 * forward list.
1408 */
1409 public void goBackOrForward(int steps) {
1410 goBackOrForward(steps, false);
1411 }
1412
1413 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
1414 // every time we go back or forward, we want to reset the
1415 // WebView certificate:
1416 // if the new site is secure, we will reload it and get a
1417 // new certificate set;
1418 // if the new site is not secure, the certificate must be
1419 // null, and that will be the case
1420 mCertificate = null;
1421 if (steps != 0) {
1422 clearTextEntry();
1423 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
1424 ignoreSnapshot ? 1 : 0);
1425 }
1426 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001428 private boolean extendScroll(int y) {
1429 int finalY = mScroller.getFinalY();
1430 int newY = pinLocY(finalY + y);
1431 if (newY == finalY) return false;
1432 mScroller.setFinalY(newY);
1433 mScroller.extendDuration(computeDuration(0, y));
1434 return true;
1435 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001436
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001437 /**
1438 * Scroll the contents of the view up by half the view size
1439 * @param top true to jump to the top of the page
1440 * @return true if the page was scrolled
1441 */
1442 public boolean pageUp(boolean top) {
1443 if (mNativeClass == 0) {
1444 return false;
1445 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001446 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001447 if (top) {
1448 // go to the top of the document
1449 return pinScrollTo(mScrollX, 0, true, 0);
1450 }
1451 // Page up
1452 int h = getHeight();
1453 int y;
1454 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1455 y = -h + PAGE_SCROLL_OVERLAP;
1456 } else {
1457 y = -h / 2;
1458 }
1459 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001460 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001461 : extendScroll(y);
1462 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001464 /**
1465 * Scroll the contents of the view down by half the page size
1466 * @param bottom true to jump to bottom of page
1467 * @return true if the page was scrolled
1468 */
1469 public boolean pageDown(boolean bottom) {
1470 if (mNativeClass == 0) {
1471 return false;
1472 }
Cary Clarke872f3a2009-06-11 09:51:11 -04001473 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001474 if (bottom) {
1475 return pinScrollTo(mScrollX, mContentHeight, true, 0);
1476 }
1477 // Page down.
1478 int h = getHeight();
1479 int y;
1480 if (h > 2 * PAGE_SCROLL_OVERLAP) {
1481 y = h - PAGE_SCROLL_OVERLAP;
1482 } else {
1483 y = h / 2;
1484 }
1485 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04001486 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001487 : extendScroll(y);
1488 }
1489
1490 /**
1491 * Clear the view so that onDraw() will draw nothing but white background,
1492 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
1493 */
1494 public void clearView() {
1495 mContentWidth = 0;
1496 mContentHeight = 0;
1497 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
1498 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001499
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001500 /**
1501 * Return a new picture that captures the current display of the webview.
1502 * This is a copy of the display, and will be unaffected if the webview
1503 * later loads a different URL.
1504 *
1505 * @return a picture containing the current contents of the view. Note this
1506 * picture is of the entire document, and is not restricted to the
1507 * bounds of the view.
1508 */
1509 public Picture capturePicture() {
Cary Clarkd6982c92009-05-29 11:02:22 -04001510 if (null == mWebViewCore) return null; // check for out of memory tab
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001511 return mWebViewCore.copyContentPicture();
1512 }
1513
1514 /**
1515 * Return true if the browser is displaying a TextView for text input.
1516 */
1517 private boolean inEditingMode() {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001518 return mWebTextView != null && mWebTextView.getParent() != null
1519 && mWebTextView.hasFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001520 }
1521
1522 private void clearTextEntry() {
1523 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04001524 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001525 }
1526 }
1527
Cary Clarkd6982c92009-05-29 11:02:22 -04001528 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001529 * Return the current scale of the WebView
1530 * @return The current scale.
1531 */
1532 public float getScale() {
1533 return mActualScale;
1534 }
1535
1536 /**
1537 * Set the initial scale for the WebView. 0 means default. If
1538 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
1539 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
1540 * WebView starts will this value as initial scale.
1541 *
1542 * @param scaleInPercent The initial scale in percent.
1543 */
1544 public void setInitialScale(int scaleInPercent) {
1545 mInitialScale = scaleInPercent;
1546 }
1547
1548 /**
1549 * Invoke the graphical zoom picker widget for this WebView. This will
1550 * result in the zoom widget appearing on the screen to control the zoom
1551 * level of this WebView.
1552 */
1553 public void invokeZoomPicker() {
1554 if (!getSettings().supportZoom()) {
1555 Log.w(LOGTAG, "This WebView doesn't support zoom.");
1556 return;
1557 }
1558 clearTextEntry();
The Android Open Source Project10592532009-03-18 17:39:46 -07001559 if (getSettings().getBuiltInZoomControls()) {
1560 mZoomButtonsController.setVisible(true);
1561 } else {
1562 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
1563 mPrivateHandler.postDelayed(mZoomControlRunnable,
1564 ZOOM_CONTROLS_TIMEOUT);
1565 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001566 }
1567
1568 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001569 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001570 * is found and the anchor has a non-javascript url, the HitTestResult type
1571 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
1572 * anchor does not have a url or if it is a javascript url, the type will
1573 * be UNKNOWN_TYPE and the url has to be retrieved through
1574 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
1575 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
1576 * the "extra" field. A type of
1577 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
1578 * a child node. If a phone number is found, the HitTestResult type is set
1579 * to PHONE_TYPE and the phone number is set in the "extra" field of
1580 * HitTestResult. If a map address is found, the HitTestResult type is set
1581 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
1582 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
1583 * and the email is set in the "extra" field of HitTestResult. Otherwise,
1584 * HitTestResult type is set to UNKNOWN_TYPE.
1585 */
1586 public HitTestResult getHitTestResult() {
1587 if (mNativeClass == 0) {
1588 return null;
1589 }
1590
1591 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04001592 if (nativeHasCursorNode()) {
1593 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001594 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04001595 } else {
1596 String text = nativeCursorText();
1597 if (text != null) {
1598 if (text.startsWith(SCHEME_TEL)) {
1599 result.setType(HitTestResult.PHONE_TYPE);
1600 result.setExtra(text.substring(SCHEME_TEL.length()));
1601 } else if (text.startsWith(SCHEME_MAILTO)) {
1602 result.setType(HitTestResult.EMAIL_TYPE);
1603 result.setExtra(text.substring(SCHEME_MAILTO.length()));
1604 } else if (text.startsWith(SCHEME_GEO)) {
1605 result.setType(HitTestResult.GEO_TYPE);
1606 result.setExtra(URLDecoder.decode(text
1607 .substring(SCHEME_GEO.length())));
1608 } else if (nativeCursorIsAnchor()) {
1609 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
1610 result.setExtra(text);
1611 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001612 }
1613 }
1614 }
1615 int type = result.getType();
1616 if (type == HitTestResult.UNKNOWN_TYPE
1617 || type == HitTestResult.SRC_ANCHOR_TYPE) {
1618 // Now check to see if it is an image.
1619 int contentX = viewToContent((int) mLastTouchX + mScrollX);
1620 int contentY = viewToContent((int) mLastTouchY + mScrollY);
1621 String text = nativeImageURI(contentX, contentY);
1622 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001623 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
1624 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001625 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
1626 result.setExtra(text);
1627 }
1628 }
1629 return result;
1630 }
1631
1632 /**
1633 * Request the href of an anchor element due to getFocusNodePath returning
1634 * "href." If hrefMsg is null, this method returns immediately and does not
1635 * dispatch hrefMsg to its target.
Cary Clarkd6982c92009-05-29 11:02:22 -04001636 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001637 * @param hrefMsg This message will be dispatched with the result of the
1638 * request as the data member with "url" as key. The result can
1639 * be null.
1640 */
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001641 // FIXME: API change required to change the name of this function. We now
1642 // look at the cursor node, and not the focus node. Also, what is
1643 // getFocusNodePath?
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001644 public void requestFocusNodeHref(Message hrefMsg) {
1645 if (hrefMsg == null || mNativeClass == 0) {
1646 return;
1647 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001648 if (nativeCursorIsAnchor()) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04001649 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
Cary Clarkd6982c92009-05-29 11:02:22 -04001650 nativeCursorFramePointer(), nativeCursorNodePointer(),
1651 hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001652 }
1653 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001654
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655 /**
1656 * Request the url of the image last touched by the user. msg will be sent
1657 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04001658 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001659 * @param msg This message will be dispatched with the result of the request
1660 * as the data member with "url" as key. The result can be null.
1661 */
1662 public void requestImageRef(Message msg) {
1663 int contentX = viewToContent((int) mLastTouchX + mScrollX);
1664 int contentY = viewToContent((int) mLastTouchY + mScrollY);
1665 String ref = nativeImageURI(contentX, contentY);
1666 Bundle data = msg.getData();
1667 data.putString("url", ref);
1668 msg.setData(data);
1669 msg.sendToTarget();
1670 }
1671
1672 private static int pinLoc(int x, int viewMax, int docMax) {
1673// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
1674 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001675 // pin the short document to the top/left of the screen
1676 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001677// Log.d(LOGTAG, "--- center " + x);
1678 } else if (x < 0) {
1679 x = 0;
1680// Log.d(LOGTAG, "--- zero");
1681 } else if (x + viewMax > docMax) {
1682 x = docMax - viewMax;
1683// Log.d(LOGTAG, "--- pin " + x);
1684 }
1685 return x;
1686 }
1687
1688 // Expects x in view coordinates
1689 private int pinLocX(int x) {
1690 return pinLoc(x, getViewWidth(), computeHorizontalScrollRange());
1691 }
1692
1693 // Expects y in view coordinates
1694 private int pinLocY(int y) {
1695 return pinLoc(y, getViewHeight(), computeVerticalScrollRange());
1696 }
1697
1698 /*package*/ int viewToContent(int x) {
1699 return Math.round(x * mInvActualScale);
1700 }
1701
Patrick Scott0a5ce012009-07-02 08:56:10 -04001702 /*package*/ int contentToView(int x) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001703 return Math.round(x * mActualScale);
1704 }
1705
1706 // Called by JNI to invalidate the View, given rectangle coordinates in
1707 // content space
1708 private void viewInvalidate(int l, int t, int r, int b) {
1709 invalidate(contentToView(l), contentToView(t), contentToView(r),
1710 contentToView(b));
1711 }
1712
1713 // Called by JNI to invalidate the View after a delay, given rectangle
1714 // coordinates in content space
1715 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
1716 postInvalidateDelayed(delay, contentToView(l), contentToView(t),
1717 contentToView(r), contentToView(b));
1718 }
1719
1720 private Rect contentToView(Rect x) {
1721 return new Rect(contentToView(x.left), contentToView(x.top)
1722 , contentToView(x.right), contentToView(x.bottom));
1723 }
1724
1725 /* call from webcoreview.draw(), so we're still executing in the UI thread
1726 */
1727 private void recordNewContentSize(int w, int h, boolean updateLayout) {
1728
1729 // premature data from webkit, ignore
1730 if ((w | h) == 0) {
1731 return;
1732 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001733
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001734 // don't abort a scroll animation if we didn't change anything
1735 if (mContentWidth != w || mContentHeight != h) {
1736 // record new dimensions
1737 mContentWidth = w;
1738 mContentHeight = h;
1739 // If history Picture is drawn, don't update scroll. They will be
1740 // updated when we get out of that mode.
1741 if (!mDrawHistory) {
1742 // repin our scroll, taking into account the new content size
1743 int oldX = mScrollX;
1744 int oldY = mScrollY;
1745 mScrollX = pinLocX(mScrollX);
1746 mScrollY = pinLocY(mScrollY);
1747 // android.util.Log.d("skia", "recordNewContentSize -
1748 // abortAnimation");
1749 mScroller.abortAnimation(); // just in case
1750 if (oldX != mScrollX || oldY != mScrollY) {
1751 sendOurVisibleRect();
1752 }
1753 }
1754 }
1755 contentSizeChanged(updateLayout);
1756 }
1757
1758 private void setNewZoomScale(float scale, boolean force) {
1759 if (scale < mMinZoomScale) {
1760 scale = mMinZoomScale;
1761 } else if (scale > mMaxZoomScale) {
1762 scale = mMaxZoomScale;
1763 }
1764 if (scale != mActualScale || force) {
1765 if (mDrawHistory) {
1766 // If history Picture is drawn, don't update scroll. They will
1767 // be updated when we get out of that mode.
1768 if (scale != mActualScale && !mPreviewZoomOnly) {
1769 mCallbackProxy.onScaleChanged(mActualScale, scale);
1770 }
1771 mActualScale = scale;
1772 mInvActualScale = 1 / scale;
1773 if (!mPreviewZoomOnly) {
1774 sendViewSizeZoom();
1775 }
1776 } else {
1777 // update our scroll so we don't appear to jump
1778 // i.e. keep the center of the doc in the center of the view
1779
1780 int oldX = mScrollX;
1781 int oldY = mScrollY;
1782 float ratio = scale * mInvActualScale; // old inverse
1783 float sx = ratio * oldX + (ratio - 1) * mZoomCenterX;
1784 float sy = ratio * oldY + (ratio - 1) * mZoomCenterY;
1785
1786 // now update our new scale and inverse
1787 if (scale != mActualScale && !mPreviewZoomOnly) {
1788 mCallbackProxy.onScaleChanged(mActualScale, scale);
1789 }
1790 mActualScale = scale;
1791 mInvActualScale = 1 / scale;
1792
Patrick Scott0a5ce012009-07-02 08:56:10 -04001793 // Scale all the child views
1794 mViewManager.scaleAll();
1795
Cary Clarkd6982c92009-05-29 11:02:22 -04001796 // as we don't have animation for scaling, don't do animation
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001797 // for scrolling, as it causes weird intermediate state
1798 // pinScrollTo(Math.round(sx), Math.round(sy));
1799 mScrollX = pinLocX(Math.round(sx));
1800 mScrollY = pinLocY(Math.round(sy));
1801
1802 if (!mPreviewZoomOnly) {
1803 sendViewSizeZoom();
1804 sendOurVisibleRect();
1805 }
1806 }
1807 }
1808 }
1809
1810 // Used to avoid sending many visible rect messages.
1811 private Rect mLastVisibleRectSent;
1812 private Rect mLastGlobalRect;
1813
1814 private Rect sendOurVisibleRect() {
1815 Rect rect = new Rect();
1816 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001817 // Rect.equals() checks for null input.
1818 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04001819 Point pos = new Point(rect.left, rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001820 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Cary Clarked56eda2009-06-18 09:48:47 -04001821 nativeMoveGeneration(), 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001822 mLastVisibleRectSent = rect;
1823 }
1824 Rect globalRect = new Rect();
1825 if (getGlobalVisibleRect(globalRect)
1826 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04001827 if (DebugFlags.WEB_VIEW) {
1828 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
1829 + globalRect.top + ",r=" + globalRect.right + ",b="
1830 + globalRect.bottom);
1831 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001832 // TODO: the global offset is only used by windowRect()
1833 // in ChromeClientAndroid ; other clients such as touch
1834 // and mouse events could return view + screen relative points.
1835 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
1836 mLastGlobalRect = globalRect;
1837 }
1838 return rect;
1839 }
1840
1841 // Sets r to be the visible rectangle of our webview in view coordinates
1842 private void calcOurVisibleRect(Rect r) {
1843 Point p = new Point();
1844 getGlobalVisibleRect(r, p);
1845 r.offset(-p.x, -p.y);
Cary Clark3524be92009-06-22 13:09:11 -04001846 if (mFindIsUp) {
1847 r.bottom -= FIND_HEIGHT;
1848 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001849 }
1850
1851 // Sets r to be our visible rectangle in content coordinates
1852 private void calcOurContentVisibleRect(Rect r) {
1853 calcOurVisibleRect(r);
1854 r.left = viewToContent(r.left);
1855 r.top = viewToContent(r.top);
1856 r.right = viewToContent(r.right);
1857 r.bottom = viewToContent(r.bottom);
1858 }
1859
1860 /**
1861 * Compute unzoomed width and height, and if they differ from the last
1862 * values we sent, send them to webkit (to be used has new viewport)
1863 *
1864 * @return true if new values were sent
1865 */
1866 private boolean sendViewSizeZoom() {
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07001867 int newWidth = Math.round(getViewWidth() * mInvActualScale);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001868 int newHeight = Math.round(getViewHeight() * mInvActualScale);
1869 /*
1870 * Because the native side may have already done a layout before the
1871 * View system was able to measure us, we have to send a height of 0 to
1872 * remove excess whitespace when we grow our width. This will trigger a
1873 * layout and a change in content size. This content size change will
1874 * mean that contentSizeChanged will either call this method directly or
1875 * indirectly from onSizeChanged.
1876 */
1877 if (newWidth > mLastWidthSent && mWrapContent) {
1878 newHeight = 0;
1879 }
1880 // Avoid sending another message if the dimensions have not changed.
1881 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
1882 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED,
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07001883 newWidth, newHeight, new Float(mActualScale));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001884 mLastWidthSent = newWidth;
1885 mLastHeightSent = newHeight;
1886 return true;
1887 }
1888 return false;
1889 }
1890
1891 @Override
1892 protected int computeHorizontalScrollRange() {
1893 if (mDrawHistory) {
1894 return mHistoryWidth;
1895 } else {
1896 return contentToView(mContentWidth);
1897 }
1898 }
1899
1900 // Make sure this stays in sync with the actual height of the FindDialog.
1901 private static final int FIND_HEIGHT = 79;
1902
1903 @Override
1904 protected int computeVerticalScrollRange() {
1905 if (mDrawHistory) {
1906 return mHistoryHeight;
1907 } else {
1908 int height = contentToView(mContentHeight);
1909 if (mFindIsUp) {
1910 height += FIND_HEIGHT;
1911 }
1912 return height;
1913 }
1914 }
1915
1916 /**
1917 * Get the url for the current page. This is not always the same as the url
1918 * passed to WebViewClient.onPageStarted because although the load for
1919 * that url has begun, the current page may not have changed.
1920 * @return The url for the current page.
1921 */
1922 public String getUrl() {
1923 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1924 return h != null ? h.getUrl() : null;
1925 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001926
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001927 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001928 * Get the original url for the current page. This is not always the same
1929 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001930 * load for that url has begun, the current page may not have changed.
1931 * Also, there may have been redirects resulting in a different url to that
1932 * originally requested.
1933 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001934 */
1935 public String getOriginalUrl() {
1936 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1937 return h != null ? h.getOriginalUrl() : null;
1938 }
1939
1940 /**
1941 * Get the title for the current page. This is the title of the current page
1942 * until WebViewClient.onReceivedTitle is called.
1943 * @return The title for the current page.
1944 */
1945 public String getTitle() {
1946 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1947 return h != null ? h.getTitle() : null;
1948 }
1949
1950 /**
1951 * Get the favicon for the current page. This is the favicon of the current
1952 * page until WebViewClient.onReceivedIcon is called.
1953 * @return The favicon for the current page.
1954 */
1955 public Bitmap getFavicon() {
1956 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
1957 return h != null ? h.getFavicon() : null;
1958 }
1959
1960 /**
1961 * Get the progress for the current page.
1962 * @return The progress for the current page between 0 and 100.
1963 */
1964 public int getProgress() {
1965 return mCallbackProxy.getProgress();
1966 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001967
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001968 /**
1969 * @return the height of the HTML content.
1970 */
1971 public int getContentHeight() {
1972 return mContentHeight;
1973 }
1974
1975 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04001976 * Pause all layout, parsing, and javascript timers for all webviews. This
1977 * is a global requests, not restricted to just this webview. This can be
1978 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001979 */
1980 public void pauseTimers() {
1981 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
1982 }
1983
1984 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04001985 * Resume all layout, parsing, and javascript timers for all webviews.
1986 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001987 */
1988 public void resumeTimers() {
1989 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
1990 }
1991
1992 /**
Mike Reedd205d5b2009-05-27 11:02:29 -04001993 * Call this to pause any extra processing associated with this view and
1994 * its associated DOM/plugins/javascript/etc. For example, if the view is
1995 * taken offscreen, this could be called to reduce unnecessary CPU and/or
1996 * network traffic. When the view is again "active", call onResume().
1997 *
1998 * Note that this differs from pauseTimers(), which affects all views/DOMs
1999 * @hide
2000 */
2001 public void onPause() {
2002 if (!mIsPaused) {
2003 mIsPaused = true;
2004 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
2005 }
2006 }
2007
2008 /**
2009 * Call this to balanace a previous call to onPause()
2010 * @hide
2011 */
2012 public void onResume() {
2013 if (mIsPaused) {
2014 mIsPaused = false;
2015 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2016 }
2017 }
2018
2019 /**
2020 * Returns true if the view is paused, meaning onPause() was called. Calling
2021 * onResume() sets the paused state back to false.
2022 * @hide
2023 */
2024 public boolean isPaused() {
2025 return mIsPaused;
2026 }
2027
2028 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002029 * Call this to inform the view that memory is low so that it can
2030 * free any available memory.
2031 * @hide
2032 */
2033 public void freeMemory() {
2034 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2035 }
2036
2037 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002038 * Clear the resource cache. Note that the cache is per-application, so
2039 * this will clear the cache for all WebViews used.
2040 *
2041 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002042 */
2043 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002044 // Note: this really needs to be a static method as it clears cache for all
2045 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2046 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002047 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2048 includeDiskFiles ? 1 : 0, 0);
2049 }
2050
2051 /**
2052 * Make sure that clearing the form data removes the adapter from the
2053 * currently focused textfield if there is one.
2054 */
2055 public void clearFormData() {
2056 if (inEditingMode()) {
2057 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002058 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002059 }
2060 }
2061
2062 /**
2063 * Tell the WebView to clear its internal back/forward list.
2064 */
2065 public void clearHistory() {
2066 mCallbackProxy.getBackForwardList().setClearPending();
2067 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2068 }
2069
2070 /**
2071 * Clear the SSL preferences table stored in response to proceeding with SSL
2072 * certificate errors.
2073 */
2074 public void clearSslPreferences() {
2075 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2076 }
2077
2078 /**
2079 * Return the WebBackForwardList for this WebView. This contains the
2080 * back/forward list for use in querying each item in the history stack.
2081 * This is a copy of the private WebBackForwardList so it contains only a
2082 * snapshot of the current state. Multiple calls to this method may return
2083 * different objects. The object returned from this method will not be
2084 * updated to reflect any new state.
2085 */
2086 public WebBackForwardList copyBackForwardList() {
2087 return mCallbackProxy.getBackForwardList().clone();
2088 }
2089
2090 /*
2091 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04002092 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002093 * calling findAll.
2094 *
2095 * @param forward Direction to search.
2096 */
2097 public void findNext(boolean forward) {
2098 nativeFindNext(forward);
2099 }
2100
2101 /*
2102 * Find all instances of find on the page and highlight them.
2103 * @param find String to find.
2104 * @return int The number of occurances of the String "find"
2105 * that were found.
2106 */
2107 public int findAll(String find) {
2108 mFindIsUp = true;
2109 int result = nativeFindAll(find.toLowerCase(), find.toUpperCase());
2110 invalidate();
2111 return result;
2112 }
2113
2114 // Used to know whether the find dialog is open. Affects whether
2115 // or not we draw the highlights for matches.
2116 private boolean mFindIsUp;
2117
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002118 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002119 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002120 * location. Currently, only addresses in the United States are detected,
2121 * and consist of:
2122 * - a house number
2123 * - a street name
2124 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2125 * - a city name
2126 * - a state or territory, either spelled out or two-letter abbr.
2127 * - an optional 5 digit or 9 digit zip code.
2128 *
2129 * All names must be correctly capitalized, and the zip code, if present,
2130 * must be valid for the state. The street type must be a standard USPS
2131 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04002132 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002133 * five digits.
2134 * @param addr The string to search for addresses.
2135 *
2136 * @return the address, or if no address is found, return null.
2137 */
2138 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04002139 return findAddress(addr, false);
2140 }
2141
2142 /**
2143 * @hide
2144 * Return the first substring consisting of the address of a physical
2145 * location. Currently, only addresses in the United States are detected,
2146 * and consist of:
2147 * - a house number
2148 * - a street name
2149 * - a street type (Road, Circle, etc), either spelled out or abbreviated
2150 * - a city name
2151 * - a state or territory, either spelled out or two-letter abbr.
2152 * - an optional 5 digit or 9 digit zip code.
2153 *
2154 * Names are optionally capitalized, and the zip code, if present,
2155 * must be valid for the state. The street type must be a standard USPS
2156 * spelling or abbreviation. The state or territory must also be spelled
2157 * or abbreviated using USPS standards. The house number may not exceed
2158 * five digits.
2159 * @param addr The string to search for addresses.
2160 * @param caseInsensitive addr Set to true to make search ignore case.
2161 *
2162 * @return the address, or if no address is found, return null.
2163 */
2164 public static String findAddress(String addr, boolean caseInsensitive) {
2165 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002166 }
2167
2168 /*
2169 * Clear the highlighting surrounding text matches created by findAll.
2170 */
2171 public void clearMatches() {
2172 mFindIsUp = false;
2173 nativeSetFindIsDown();
2174 // Now that the dialog has been removed, ensure that we scroll to a
2175 // location that is not beyond the end of the page.
2176 pinScrollTo(mScrollX, mScrollY, false, 0);
2177 invalidate();
2178 }
2179
2180 /**
2181 * Query the document to see if it contains any image references. The
2182 * message object will be dispatched with arg1 being set to 1 if images
2183 * were found and 0 if the document does not reference any images.
2184 * @param response The message that will be dispatched with the result.
2185 */
2186 public void documentHasImages(Message response) {
2187 if (response == null) {
2188 return;
2189 }
2190 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
2191 }
2192
2193 @Override
2194 public void computeScroll() {
2195 if (mScroller.computeScrollOffset()) {
2196 int oldX = mScrollX;
2197 int oldY = mScrollY;
2198 mScrollX = mScroller.getCurrX();
2199 mScrollY = mScroller.getCurrY();
2200 postInvalidate(); // So we draw again
2201 if (oldX != mScrollX || oldY != mScrollY) {
2202 // as onScrollChanged() is not called, sendOurVisibleRect()
2203 // needs to be call explicitly
2204 sendOurVisibleRect();
2205 }
2206 } else {
2207 super.computeScroll();
2208 }
2209 }
2210
2211 private static int computeDuration(int dx, int dy) {
2212 int distance = Math.max(Math.abs(dx), Math.abs(dy));
2213 int duration = distance * 1000 / STD_SPEED;
2214 return Math.min(duration, MAX_DURATION);
2215 }
2216
2217 // helper to pin the scrollBy parameters (already in view coordinates)
2218 // returns true if the scroll was changed
2219 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
2220 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
2221 }
2222
2223 // helper to pin the scrollTo parameters (already in view coordinates)
2224 // returns true if the scroll was changed
2225 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
2226 x = pinLocX(x);
2227 y = pinLocY(y);
2228 int dx = x - mScrollX;
2229 int dy = y - mScrollY;
2230
2231 if ((dx | dy) == 0) {
2232 return false;
2233 }
2234
2235 if (true && animate) {
2236 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
2237
2238 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
2239 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
2240 invalidate();
2241 } else {
2242 mScroller.abortAnimation(); // just in case
2243 scrollTo(x, y);
2244 }
2245 return true;
2246 }
2247
2248 // Scale from content to view coordinates, and pin.
2249 // Also called by jni webview.cpp
2250 private void setContentScrollBy(int cx, int cy, boolean animate) {
2251 if (mDrawHistory) {
2252 // disallow WebView to change the scroll position as History Picture
2253 // is used in the view system.
2254 // TODO: as we switchOutDrawHistory when trackball or navigation
2255 // keys are hit, this should be safe. Right?
2256 return;
2257 }
2258 cx = contentToView(cx);
2259 cy = contentToView(cy);
2260 if (mHeightCanMeasure) {
2261 // move our visible rect according to scroll request
2262 if (cy != 0) {
2263 Rect tempRect = new Rect();
2264 calcOurVisibleRect(tempRect);
2265 tempRect.offset(cx, cy);
2266 requestRectangleOnScreen(tempRect);
2267 }
2268 // FIXME: We scroll horizontally no matter what because currently
2269 // ScrollView and ListView will not scroll horizontally.
2270 // FIXME: Why do we only scroll horizontally if there is no
2271 // vertical scroll?
2272// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
2273 if (cy == 0 && cx != 0) {
2274 pinScrollBy(cx, 0, animate, 0);
2275 }
2276 } else {
2277 pinScrollBy(cx, cy, animate, 0);
2278 }
2279 }
2280
2281 // scale from content to view coordinates, and pin
2282 // return true if pin caused the final x/y different than the request cx/cy;
2283 // return false if the view scroll to the exact position as it is requested.
2284 private boolean setContentScrollTo(int cx, int cy) {
2285 if (mDrawHistory) {
2286 // disallow WebView to change the scroll position as History Picture
2287 // is used in the view system.
2288 // One known case where this is called is that WebCore tries to
2289 // restore the scroll position. As history Picture already uses the
2290 // saved scroll position, it is ok to skip this.
2291 return false;
2292 }
2293 int vx = contentToView(cx);
2294 int vy = contentToView(cy);
2295// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
2296// vx + " " + vy + "]");
2297 pinScrollTo(vx, vy, false, 0);
2298 if (mScrollX != vx || mScrollY != vy) {
2299 return true;
2300 } else {
2301 return false;
2302 }
2303 }
2304
2305 // scale from content to view coordinates, and pin
2306 private void spawnContentScrollTo(int cx, int cy) {
2307 if (mDrawHistory) {
2308 // disallow WebView to change the scroll position as History Picture
2309 // is used in the view system.
2310 return;
2311 }
2312 int vx = contentToView(cx);
2313 int vy = contentToView(cy);
2314 pinScrollTo(vx, vy, true, 0);
2315 }
2316
2317 /**
2318 * These are from webkit, and are in content coordinate system (unzoomed)
2319 */
2320 private void contentSizeChanged(boolean updateLayout) {
2321 // suppress 0,0 since we usually see real dimensions soon after
2322 // this avoids drawing the prev content in a funny place. If we find a
2323 // way to consolidate these notifications, this check may become
2324 // obsolete
2325 if ((mContentWidth | mContentHeight) == 0) {
2326 return;
2327 }
2328
2329 if (mHeightCanMeasure) {
2330 if (getMeasuredHeight() != contentToView(mContentHeight)
2331 && updateLayout) {
2332 requestLayout();
2333 }
2334 } else if (mWidthCanMeasure) {
2335 if (getMeasuredWidth() != contentToView(mContentWidth)
2336 && updateLayout) {
2337 requestLayout();
2338 }
2339 } else {
2340 // If we don't request a layout, try to send our view size to the
2341 // native side to ensure that WebCore has the correct dimensions.
2342 sendViewSizeZoom();
2343 }
2344 }
2345
2346 /**
2347 * Set the WebViewClient that will receive various notifications and
2348 * requests. This will replace the current handler.
2349 * @param client An implementation of WebViewClient.
2350 */
2351 public void setWebViewClient(WebViewClient client) {
2352 mCallbackProxy.setWebViewClient(client);
2353 }
2354
2355 /**
2356 * Register the interface to be used when content can not be handled by
2357 * the rendering engine, and should be downloaded instead. This will replace
2358 * the current handler.
2359 * @param listener An implementation of DownloadListener.
2360 */
2361 public void setDownloadListener(DownloadListener listener) {
2362 mCallbackProxy.setDownloadListener(listener);
2363 }
2364
2365 /**
2366 * Set the chrome handler. This is an implementation of WebChromeClient for
2367 * use in handling Javascript dialogs, favicons, titles, and the progress.
2368 * This will replace the current handler.
2369 * @param client An implementation of WebChromeClient.
2370 */
2371 public void setWebChromeClient(WebChromeClient client) {
2372 mCallbackProxy.setWebChromeClient(client);
2373 }
2374
2375 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01002376 * Gets the chrome handler.
2377 * @return the current WebChromeClient instance.
2378 *
2379 * @hide API council approval.
2380 */
2381 public WebChromeClient getWebChromeClient() {
2382 return mCallbackProxy.getWebChromeClient();
2383 }
2384
2385 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002386 * Set the Picture listener. This is an interface used to receive
2387 * notifications of a new Picture.
2388 * @param listener An implementation of WebView.PictureListener.
2389 */
2390 public void setPictureListener(PictureListener listener) {
2391 mPictureListener = listener;
2392 }
2393
2394 /**
2395 * {@hide}
2396 */
2397 /* FIXME: Debug only! Remove for SDK! */
2398 public void externalRepresentation(Message callback) {
2399 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
2400 }
2401
2402 /**
2403 * {@hide}
2404 */
2405 /* FIXME: Debug only! Remove for SDK! */
2406 public void documentAsText(Message callback) {
2407 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
2408 }
2409
2410 /**
2411 * Use this function to bind an object to Javascript so that the
2412 * methods can be accessed from Javascript.
2413 * <p><strong>IMPORTANT:</strong>
2414 * <ul>
2415 * <li> Using addJavascriptInterface() allows JavaScript to control your
2416 * application. This can be a very useful feature or a dangerous security
2417 * issue. When the HTML in the WebView is untrustworthy (for example, part
2418 * or all of the HTML is provided by some person or process), then an
2419 * attacker could inject HTML that will execute your code and possibly any
2420 * code of the attacker's choosing.<br>
2421 * Do not use addJavascriptInterface() unless all of the HTML in this
2422 * WebView was written by you.</li>
2423 * <li> The Java object that is bound runs in another thread and not in
2424 * the thread that it was constructed in.</li>
2425 * </ul></p>
2426 * @param obj The class instance to bind to Javascript
2427 * @param interfaceName The name to used to expose the class in Javascript
2428 */
2429 public void addJavascriptInterface(Object obj, String interfaceName) {
Cary Clarkded054c2009-06-15 10:26:08 -04002430 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
2431 arg.mObject = obj;
2432 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002433 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
2434 }
2435
2436 /**
2437 * Return the WebSettings object used to control the settings for this
2438 * WebView.
2439 * @return A WebSettings object that can be used to control this WebView's
2440 * settings.
2441 */
2442 public WebSettings getSettings() {
2443 return mWebViewCore.getSettings();
2444 }
2445
2446 /**
2447 * Return the list of currently loaded plugins.
2448 * @return The list of currently loaded plugins.
2449 */
2450 public static synchronized PluginList getPluginList() {
2451 if (sPluginList == null) {
2452 sPluginList = new PluginList();
2453 }
2454 return sPluginList;
2455 }
2456
2457 /**
Grace Kloba658ab7d2009-05-14 14:45:26 -07002458 * TODO: need to add @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002459 */
2460 public void refreshPlugins(boolean reloadOpenPages) {
Grace Kloba658ab7d2009-05-14 14:45:26 -07002461 PluginManager.getInstance(mContext).refreshPlugins(reloadOpenPages);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002462 }
2463
2464 //-------------------------------------------------------------------------
2465 // Override View methods
2466 //-------------------------------------------------------------------------
2467
2468 @Override
2469 protected void finalize() throws Throwable {
2470 destroy();
2471 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002472
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002473 @Override
2474 protected void onDraw(Canvas canvas) {
2475 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
2476 if (mNativeClass == 0) {
2477 return;
2478 }
2479 if (mWebViewCore.mEndScaleZoom) {
2480 mWebViewCore.mEndScaleZoom = false;
Cary Clarkd6982c92009-05-29 11:02:22 -04002481 if (mTouchMode >= FIRST_SCROLL_ZOOM
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002482 && mTouchMode <= LAST_SCROLL_ZOOM) {
2483 setHorizontalScrollBarEnabled(true);
2484 setVerticalScrollBarEnabled(true);
2485 mTouchMode = TOUCH_DONE_MODE;
2486 }
2487 }
2488 int sc = canvas.save();
2489 if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
2490 scrollZoomDraw(canvas);
2491 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002492 // Update the buttons in the picture, so when we draw the picture
2493 // to the screen, they are in the correct state.
2494 // Tell the native side if user is a) touching the screen,
2495 // b) pressing the trackball down, or c) pressing the enter key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002496 // If the cursor is on a button, we need to draw it in the pressed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002497 // state.
2498 // If mNativeClass is 0, we should not reach here, so we do not
2499 // need to check it again.
2500 nativeRecordButtons(hasFocus() && hasWindowFocus(),
2501 mTouchMode == TOUCH_SHORTPRESS_START_MODE
Leon Scrogginse3225672009-06-03 15:53:13 -04002502 || mTrackballDown || mGotCenterDown, false);
Cary Clarkd6982c92009-05-29 11:02:22 -04002503 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002504 }
2505 canvas.restoreToCount(sc);
Cary Clarkd6982c92009-05-29 11:02:22 -04002506
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002507 if (AUTO_REDRAW_HACK && mAutoRedraw) {
2508 invalidate();
2509 }
2510 }
2511
2512 @Override
2513 public void setLayoutParams(ViewGroup.LayoutParams params) {
2514 if (params.height == LayoutParams.WRAP_CONTENT) {
2515 mWrapContent = true;
2516 }
2517 super.setLayoutParams(params);
2518 }
2519
2520 @Override
2521 public boolean performLongClick() {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04002522 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
2523 // Send the click so that the textfield is in focus
2524 // FIXME: When we start respecting changes to the native textfield's
2525 // selection, need to make sure that this does not change it.
2526 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
2527 nativeCursorNodePointer());
2528 rebuildWebTextView();
2529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002530 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002531 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002532 } else {
2533 return super.performLongClick();
2534 }
2535 }
2536
Cary Clarkd6982c92009-05-29 11:02:22 -04002537 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002538 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002539 if (mDrawHistory) {
2540 canvas.scale(mActualScale, mActualScale);
2541 canvas.drawPicture(mHistoryPicture);
2542 return;
2543 }
2544
2545 boolean animateZoom = mZoomScale != 0;
Cary Clarkd6982c92009-05-29 11:02:22 -04002546 boolean animateScroll = !mScroller.isFinished()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002547 || mVelocityTracker != null;
2548 if (animateZoom) {
2549 float zoomScale;
2550 int interval = (int) (SystemClock.uptimeMillis() - mZoomStart);
2551 if (interval < ZOOM_ANIMATION_LENGTH) {
2552 float ratio = (float) interval / ZOOM_ANIMATION_LENGTH;
Cary Clarkd6982c92009-05-29 11:02:22 -04002553 zoomScale = 1.0f / (mInvInitialZoomScale
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002554 + (mInvFinalZoomScale - mInvInitialZoomScale) * ratio);
2555 invalidate();
2556 } else {
2557 zoomScale = mZoomScale;
2558 // set mZoomScale to be 0 as we have done animation
2559 mZoomScale = 0;
2560 }
2561 float scale = (mActualScale - zoomScale) * mInvActualScale;
2562 float tx = scale * (mZoomCenterX + mScrollX);
2563 float ty = scale * (mZoomCenterY + mScrollY);
2564
2565 // this block pins the translate to "legal" bounds. This makes the
2566 // animation a bit non-obvious, but it means we won't pop when the
2567 // "real" zoom takes effect
2568 if (true) {
2569 // canvas.translate(mScrollX, mScrollY);
2570 tx -= mScrollX;
2571 ty -= mScrollY;
2572 tx = -pinLoc(-Math.round(tx), getViewWidth(), Math
2573 .round(mContentWidth * zoomScale));
2574 ty = -pinLoc(-Math.round(ty), getViewHeight(), Math
2575 .round(mContentHeight * zoomScale));
2576 tx += mScrollX;
2577 ty += mScrollY;
2578 }
2579 canvas.translate(tx, ty);
2580 canvas.scale(zoomScale, zoomScale);
2581 } else {
2582 canvas.scale(mActualScale, mActualScale);
2583 }
2584
2585 mWebViewCore.drawContentPicture(canvas, color, animateZoom,
2586 animateScroll);
2587
2588 if (mNativeClass == 0) return;
2589 if (mShiftIsPressed) {
2590 if (mTouchSelection) {
2591 nativeDrawSelectionRegion(canvas);
2592 } else {
Cary Clarkd6982c92009-05-29 11:02:22 -04002593 nativeDrawSelection(canvas, mSelectX, mSelectY,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002594 mExtendSelection);
2595 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002596 } else if (drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002597 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
2598 mTouchMode = TOUCH_SHORTPRESS_MODE;
2599 HitTestResult hitTest = getHitTestResult();
2600 if (hitTest != null &&
2601 hitTest.mType != HitTestResult.UNKNOWN_TYPE) {
2602 mPrivateHandler.sendMessageDelayed(mPrivateHandler
2603 .obtainMessage(SWITCH_TO_LONGPRESS),
2604 LONG_PRESS_TIMEOUT);
2605 }
2606 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002607 nativeDrawCursorRing(canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002608 }
2609 // When the FindDialog is up, only draw the matches if we are not in
2610 // the process of scrolling them into view.
2611 if (mFindIsUp && !animateScroll) {
2612 nativeDrawMatches(canvas);
2613 }
2614 }
2615
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002616 private float scrollZoomGridScale(float invScale) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002617 float griddedInvScale = (int) (invScale * SCROLL_ZOOM_GRID)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002618 / (float) SCROLL_ZOOM_GRID;
2619 return 1.0f / griddedInvScale;
2620 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002621
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002622 private float scrollZoomX(float scale) {
2623 int width = getViewWidth();
2624 float maxScrollZoomX = mContentWidth * scale - width;
2625 int maxX = mContentWidth - width;
2626 return -(maxScrollZoomX > 0 ? mZoomScrollX * maxScrollZoomX / maxX
2627 : maxScrollZoomX / 2);
2628 }
2629
2630 private float scrollZoomY(float scale) {
2631 int height = getViewHeight();
2632 float maxScrollZoomY = mContentHeight * scale - height;
2633 int maxY = mContentHeight - height;
2634 return -(maxScrollZoomY > 0 ? mZoomScrollY * maxScrollZoomY / maxY
2635 : maxScrollZoomY / 2);
2636 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002637
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002638 private void drawMagnifyFrame(Canvas canvas, Rect frame, Paint paint) {
2639 final float ADORNMENT_LEN = 16.0f;
2640 float width = frame.width();
2641 float height = frame.height();
2642 Path path = new Path();
2643 path.moveTo(-ADORNMENT_LEN, -ADORNMENT_LEN);
2644 path.lineTo(0, 0);
2645 path.lineTo(width, 0);
2646 path.lineTo(width + ADORNMENT_LEN, -ADORNMENT_LEN);
2647 path.moveTo(-ADORNMENT_LEN, height + ADORNMENT_LEN);
2648 path.lineTo(0, height);
2649 path.lineTo(width, height);
2650 path.lineTo(width + ADORNMENT_LEN, height + ADORNMENT_LEN);
2651 path.moveTo(0, 0);
2652 path.lineTo(0, height);
2653 path.moveTo(width, 0);
2654 path.lineTo(width, height);
2655 path.offset(frame.left, frame.top);
2656 canvas.drawPath(path, paint);
2657 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002658
2659 // Returns frame surrounding magified portion of screen while
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002660 // scroll-zoom is enabled. The frame is also used to center the
2661 // zoom-in zoom-out points at the start and end of the animation.
2662 private Rect scrollZoomFrame(int width, int height, float halfScale) {
2663 Rect scrollFrame = new Rect();
Cary Clarkd6982c92009-05-29 11:02:22 -04002664 scrollFrame.set(mZoomScrollX, mZoomScrollY,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002665 mZoomScrollX + width, mZoomScrollY + height);
2666 if (mContentWidth * mZoomScrollLimit < width) {
2667 float scale = zoomFrameScaleX(width, halfScale, 1.0f);
2668 float offsetX = (width * scale - width) * 0.5f;
2669 scrollFrame.left -= offsetX;
2670 scrollFrame.right += offsetX;
2671 }
2672 if (mContentHeight * mZoomScrollLimit < height) {
2673 float scale = zoomFrameScaleY(height, halfScale, 1.0f);
2674 float offsetY = (height * scale - height) * 0.5f;
2675 scrollFrame.top -= offsetY;
2676 scrollFrame.bottom += offsetY;
2677 }
2678 return scrollFrame;
2679 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002680
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002681 private float zoomFrameScaleX(int width, float halfScale, float noScale) {
2682 // mContentWidth > width > mContentWidth * mZoomScrollLimit
2683 if (mContentWidth <= width) {
2684 return halfScale;
2685 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002686 float part = (width - mContentWidth * mZoomScrollLimit)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002687 / (width * (1 - mZoomScrollLimit));
2688 return halfScale * part + noScale * (1.0f - part);
2689 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002690
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002691 private float zoomFrameScaleY(int height, float halfScale, float noScale) {
2692 if (mContentHeight <= height) {
2693 return halfScale;
2694 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002695 float part = (height - mContentHeight * mZoomScrollLimit)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002696 / (height * (1 - mZoomScrollLimit));
2697 return halfScale * part + noScale * (1.0f - part);
2698 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002699
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002700 private float scrollZoomMagScale(float invScale) {
2701 return (invScale * 2 + mInvActualScale) / 3;
2702 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002703
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002704 private void scrollZoomDraw(Canvas canvas) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002705 float invScale = mZoomScrollInvLimit;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002706 int elapsed = 0;
2707 if (mTouchMode != SCROLL_ZOOM_OUT) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002708 elapsed = (int) Math.min(System.currentTimeMillis()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002709 - mZoomScrollStart, SCROLL_ZOOM_DURATION);
Cary Clarkd6982c92009-05-29 11:02:22 -04002710 float transitionScale = (mZoomScrollInvLimit - mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002711 * elapsed / SCROLL_ZOOM_DURATION;
2712 if (mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
2713 invScale = mInvActualScale + transitionScale;
2714 } else { /* if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) */
2715 invScale = mZoomScrollInvLimit - transitionScale;
2716 }
2717 }
2718 float scale = scrollZoomGridScale(invScale);
2719 invScale = 1.0f / scale;
2720 int width = getViewWidth();
2721 int height = getViewHeight();
2722 float halfScale = scrollZoomMagScale(invScale);
2723 Rect scrollFrame = scrollZoomFrame(width, height, halfScale);
2724 if (elapsed == SCROLL_ZOOM_DURATION) {
2725 if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
2726 setHorizontalScrollBarEnabled(true);
2727 setVerticalScrollBarEnabled(true);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002728 rebuildWebTextView();
Cary Clarkd6982c92009-05-29 11:02:22 -04002729 scrollTo((int) (scrollFrame.centerX() * mActualScale)
2730 - (width >> 1), (int) (scrollFrame.centerY()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002731 * mActualScale) - (height >> 1));
2732 mTouchMode = TOUCH_DONE_MODE;
Patrick Scott0a5ce012009-07-02 08:56:10 -04002733 // Show all the child views once we are done.
2734 mViewManager.showAll();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002735 } else {
2736 mTouchMode = SCROLL_ZOOM_OUT;
2737 }
2738 }
2739 float newX = scrollZoomX(scale);
2740 float newY = scrollZoomY(scale);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002741 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002742 Log.v(LOGTAG, "scrollZoomDraw scale=" + scale + " + (" + newX
2743 + ", " + newY + ") mZoomScroll=(" + mZoomScrollX + ", "
Cary Clarkd6982c92009-05-29 11:02:22 -04002744 + mZoomScrollY + ")" + " invScale=" + invScale + " scale="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002745 + scale);
2746 }
2747 canvas.translate(newX, newY);
2748 canvas.scale(scale, scale);
2749 boolean animating = mTouchMode != SCROLL_ZOOM_OUT;
2750 if (mDrawHistory) {
2751 int sc = canvas.save(Canvas.CLIP_SAVE_FLAG);
2752 Rect clip = new Rect(0, 0, mHistoryPicture.getWidth(),
2753 mHistoryPicture.getHeight());
2754 canvas.clipRect(clip, Region.Op.DIFFERENCE);
2755 canvas.drawColor(mBackgroundColor);
2756 canvas.restoreToCount(sc);
2757 canvas.drawPicture(mHistoryPicture);
2758 } else {
2759 mWebViewCore.drawContentPicture(canvas, mBackgroundColor,
2760 animating, true);
2761 }
2762 if (mTouchMode == TOUCH_DONE_MODE) {
2763 return;
2764 }
2765 Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
2766 paint.setStyle(Paint.Style.STROKE);
2767 paint.setStrokeWidth(30.0f);
2768 paint.setARGB(0x50, 0, 0, 0);
2769 int maxX = mContentWidth - width;
2770 int maxY = mContentHeight - height;
2771 if (true) { // experiment: draw hint to place finger off magnify area
2772 drawMagnifyFrame(canvas, scrollFrame, paint);
2773 } else {
2774 canvas.drawRect(scrollFrame, paint);
2775 }
2776 int sc = canvas.save();
2777 canvas.clipRect(scrollFrame);
2778 float halfX = (float) mZoomScrollX / maxX;
2779 if (mContentWidth * mZoomScrollLimit < width) {
2780 halfX = zoomFrameScaleX(width, 0.5f, halfX);
2781 }
2782 float halfY = (float) mZoomScrollY / maxY;
2783 if (mContentHeight * mZoomScrollLimit < height) {
2784 halfY = zoomFrameScaleY(height, 0.5f, halfY);
2785 }
2786 canvas.scale(halfScale, halfScale, mZoomScrollX + width * halfX
2787 , mZoomScrollY + height * halfY);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002788 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002789 Log.v(LOGTAG, "scrollZoomDraw halfScale=" + halfScale + " w/h=("
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002790 + width + ", " + height + ") half=(" + halfX + ", "
2791 + halfY + ")");
2792 }
2793 if (mDrawHistory) {
2794 canvas.drawPicture(mHistoryPicture);
2795 } else {
2796 mWebViewCore.drawContentPicture(canvas, mBackgroundColor,
2797 animating, false);
2798 }
2799 canvas.restoreToCount(sc);
2800 if (mTouchMode != SCROLL_ZOOM_OUT) {
2801 invalidate();
2802 }
2803 }
2804
2805 private void zoomScrollTap(float x, float y) {
2806 float scale = scrollZoomGridScale(mZoomScrollInvLimit);
2807 float left = scrollZoomX(scale);
2808 float top = scrollZoomY(scale);
2809 int width = getViewWidth();
2810 int height = getViewHeight();
2811 x -= width * scale / 2;
2812 y -= height * scale / 2;
2813 mZoomScrollX = Math.min(mContentWidth - width
2814 , Math.max(0, (int) ((x - left) / scale)));
2815 mZoomScrollY = Math.min(mContentHeight - height
2816 , Math.max(0, (int) ((y - top) / scale)));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002817 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002818 Log.v(LOGTAG, "zoomScrollTap scale=" + scale + " + (" + left
2819 + ", " + top + ") mZoomScroll=(" + mZoomScrollX + ", "
2820 + mZoomScrollY + ")" + " x=" + x + " y=" + y);
2821 }
2822 }
2823
Leon Scrogginsa5645b22009-06-09 15:31:19 -04002824 /**
2825 * @hide
2826 */
2827 public boolean canZoomScrollOut() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002828 if (mContentWidth == 0 || mContentHeight == 0) {
2829 return false;
2830 }
2831 int width = getViewWidth();
2832 int height = getViewHeight();
2833 float x = (float) width / (float) mContentWidth;
2834 float y = (float) height / (float) mContentHeight;
2835 mZoomScrollLimit = Math.max(DEFAULT_MIN_ZOOM_SCALE, Math.min(x, y));
2836 mZoomScrollInvLimit = 1.0f / mZoomScrollLimit;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002837 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002838 Log.v(LOGTAG, "canZoomScrollOut"
2839 + " mInvActualScale=" + mInvActualScale
2840 + " mZoomScrollLimit=" + mZoomScrollLimit
2841 + " mZoomScrollInvLimit=" + mZoomScrollInvLimit
2842 + " mContentWidth=" + mContentWidth
2843 + " mContentHeight=" + mContentHeight
2844 );
2845 }
2846 // don't zoom out unless magnify area is at least half as wide
2847 // or tall as content
2848 float limit = mZoomScrollLimit * 2;
2849 return mContentWidth >= width * limit
2850 || mContentHeight >= height * limit;
2851 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002852
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002853 private void startZoomScrollOut() {
2854 setHorizontalScrollBarEnabled(false);
2855 setVerticalScrollBarEnabled(false);
The Android Open Source Project10592532009-03-18 17:39:46 -07002856 if (getSettings().getBuiltInZoomControls()) {
2857 if (mZoomButtonsController.isVisible()) {
2858 mZoomButtonsController.setVisible(false);
2859 }
2860 } else {
2861 if (mZoomControlRunnable != null) {
2862 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
2863 }
2864 if (mZoomControls != null) {
2865 mZoomControls.hide();
2866 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002867 }
2868 int width = getViewWidth();
2869 int height = getViewHeight();
2870 int halfW = width >> 1;
2871 mLastTouchX = halfW;
2872 int halfH = height >> 1;
2873 mLastTouchY = halfH;
2874 mScroller.abortAnimation();
2875 mZoomScrollStart = System.currentTimeMillis();
2876 Rect zoomFrame = scrollZoomFrame(width, height
2877 , scrollZoomMagScale(mZoomScrollInvLimit));
Cary Clarkd6982c92009-05-29 11:02:22 -04002878 mZoomScrollX = Math.max(0, (int) ((mScrollX + halfW) * mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002879 - (zoomFrame.width() >> 1));
Cary Clarkd6982c92009-05-29 11:02:22 -04002880 mZoomScrollY = Math.max(0, (int) ((mScrollY + halfH) * mInvActualScale)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002881 - (zoomFrame.height() >> 1));
2882 scrollTo(0, 0); // triggers inval, starts animation
2883 clearTextEntry();
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002884 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002885 Log.v(LOGTAG, "startZoomScrollOut mZoomScroll=("
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002886 + mZoomScrollX + ", " + mZoomScrollY +")");
2887 }
2888 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002889
Leon Scrogginsa5645b22009-06-09 15:31:19 -04002890 /**
2891 * @hide
2892 */
2893 public void zoomScrollOut() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002894 if (canZoomScrollOut() == false) {
2895 mTouchMode = TOUCH_DONE_MODE;
2896 return;
2897 }
Patrick Scott0a5ce012009-07-02 08:56:10 -04002898 // Hide the child views while in this mode.
2899 mViewManager.hideAll();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002900 startZoomScrollOut();
2901 mTouchMode = SCROLL_ZOOM_ANIMATION_OUT;
2902 invalidate();
2903 }
2904
2905 private void moveZoomScrollWindow(float x, float y) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002906 if (Math.abs(x - mLastZoomScrollRawX) < 1.5f
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002907 && Math.abs(y - mLastZoomScrollRawY) < 1.5f) {
2908 return;
2909 }
2910 mLastZoomScrollRawX = x;
2911 mLastZoomScrollRawY = y;
2912 int oldX = mZoomScrollX;
2913 int oldY = mZoomScrollY;
2914 int width = getViewWidth();
2915 int height = getViewHeight();
2916 int maxZoomX = mContentWidth - width;
2917 if (maxZoomX > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002918 int maxScreenX = width - (int) Math.ceil(width
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002919 * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002920 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002921 Log.v(LOGTAG, "moveZoomScrollWindow-X"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002922 + " maxScreenX=" + maxScreenX + " width=" + width
Cary Clarkd6982c92009-05-29 11:02:22 -04002923 + " mZoomScrollLimit=" + mZoomScrollLimit + " x=" + x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002924 }
2925 x += maxScreenX * mLastScrollX / maxZoomX - mLastTouchX;
2926 x *= Math.max(maxZoomX / maxScreenX, mZoomScrollInvLimit);
2927 mZoomScrollX = Math.max(0, Math.min(maxZoomX, (int) x));
2928 }
2929 int maxZoomY = mContentHeight - height;
2930 if (maxZoomY > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002931 int maxScreenY = height - (int) Math.ceil(height
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002932 * mZoomScrollLimit) - SCROLL_ZOOM_FINGER_BUFFER;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002933 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002934 Log.v(LOGTAG, "moveZoomScrollWindow-Y"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002935 + " maxScreenY=" + maxScreenY + " height=" + height
Cary Clarkd6982c92009-05-29 11:02:22 -04002936 + " mZoomScrollLimit=" + mZoomScrollLimit + " y=" + y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002937 }
2938 y += maxScreenY * mLastScrollY / maxZoomY - mLastTouchY;
2939 y *= Math.max(maxZoomY / maxScreenY, mZoomScrollInvLimit);
2940 mZoomScrollY = Math.max(0, Math.min(maxZoomY, (int) y));
2941 }
2942 if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
2943 invalidate();
2944 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04002945 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002946 Log.v(LOGTAG, "moveZoomScrollWindow"
2947 + " scrollTo=(" + mZoomScrollX + ", " + mZoomScrollY + ")"
2948 + " mLastTouch=(" + mLastTouchX + ", " + mLastTouchY + ")"
2949 + " maxZoom=(" + maxZoomX + ", " + maxZoomY + ")"
2950 + " last=("+mLastScrollX+", "+mLastScrollY+")"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002951 + " x=" + x + " y=" + y);
2952 }
2953 }
2954
2955 private void setZoomScrollIn() {
2956 mZoomScrollStart = System.currentTimeMillis();
2957 }
2958
2959 private float mZoomScrollLimit;
2960 private float mZoomScrollInvLimit;
2961 private int mLastScrollX;
2962 private int mLastScrollY;
2963 private long mZoomScrollStart;
2964 private int mZoomScrollX;
2965 private int mZoomScrollY;
2966 private float mLastZoomScrollRawX = -1000.0f;
2967 private float mLastZoomScrollRawY = -1000.0f;
2968 // The zoomed scale varies from 1.0 to DEFAULT_MIN_ZOOM_SCALE == 0.25.
2969 // The zoom animation duration SCROLL_ZOOM_DURATION == 0.5.
2970 // Two pressures compete for gridding; a high frame rate (e.g. 20 fps)
2971 // and minimizing font cache allocations (fewer frames is better).
2972 // A SCROLL_ZOOM_GRID of 6 permits about 20 zoom levels over 0.5 seconds:
2973 // the inverse of: 1.0, 1.16, 1.33, 1.5, 1.67, 1.84, 2.0, etc. to 4.0
2974 private static final int SCROLL_ZOOM_GRID = 6;
2975 private static final int SCROLL_ZOOM_DURATION = 500;
2976 // Make it easier to get to the bottom of a document by reserving a 32
2977 // pixel buffer, for when the starting drag is a bit below the bottom of
2978 // the magnify frame.
2979 private static final int SCROLL_ZOOM_FINGER_BUFFER = 32;
2980
2981 // draw history
2982 private boolean mDrawHistory = false;
2983 private Picture mHistoryPicture = null;
2984 private int mHistoryWidth = 0;
2985 private int mHistoryHeight = 0;
2986
2987 // Only check the flag, can be called from WebCore thread
2988 boolean drawHistory() {
2989 return mDrawHistory;
2990 }
2991
2992 // Should only be called in UI thread
2993 void switchOutDrawHistory() {
2994 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Cary Clarkbc2e33b2009-04-23 13:12:19 -04002995 if (mDrawHistory && mWebViewCore.pictureReady()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002996 mDrawHistory = false;
2997 invalidate();
2998 int oldScrollX = mScrollX;
2999 int oldScrollY = mScrollY;
3000 mScrollX = pinLocX(mScrollX);
3001 mScrollY = pinLocY(mScrollY);
3002 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
3003 mUserScroll = false;
3004 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL, oldScrollX,
3005 oldScrollY);
3006 }
3007 sendOurVisibleRect();
3008 }
3009 }
3010
Cary Clarkd6982c92009-05-29 11:02:22 -04003011 WebViewCore.CursorData cursorData() {
3012 WebViewCore.CursorData result = new WebViewCore.CursorData();
3013 result.mMoveGeneration = nativeMoveGeneration();
3014 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04003015 Point position = nativeCursorPosition();
3016 result.mX = position.x;
3017 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04003018 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003019 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003020
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003021 /**
3022 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04003023 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003024 * order, swap them.
3025 * @param start Beginning of selection to delete.
3026 * @param end End of selection to delete.
3027 */
3028 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003029 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04003030 WebViewCore.DeleteSelectionData data
3031 = new WebViewCore.DeleteSelectionData();
3032 data.mStart = start;
3033 data.mEnd = end;
3034 data.mTextGeneration = mTextGeneration;
3035 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003036 }
3037
3038 /**
3039 * Set the selection to (start, end) in the focused textfield. If start and
3040 * end are out of order, swap them.
3041 * @param start Beginning of selection.
3042 * @param end End of selection.
3043 */
3044 /* package */ void setSelection(int start, int end) {
Cary Clark2f1d60c2009-06-03 08:05:53 -04003045 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003046 }
3047
3048 // Called by JNI when a touch event puts a textfield into focus.
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003049 private void displaySoftKeyboard(boolean isTextView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003050 InputMethodManager imm = (InputMethodManager)
3051 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003052
3053 if (isTextView) {
3054 imm.showSoftInput(mWebTextView, 0);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003055 // Now we need to fake a touch event to place the cursor where the
3056 // user touched.
3057 AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
3058 mWebTextView.getLayoutParams();
3059 if (lp != null) {
3060 // Take the last touch and adjust for the location of the
3061 // WebTextView.
3062 float x = mLastTouchX + (float) (mScrollX - lp.x);
3063 float y = mLastTouchY + (float) (mScrollY - lp.y);
3064 mWebTextView.fakeTouchEvent(x, y);
3065 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003066 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04003067 else { // used by plugins
3068 imm.showSoftInput(this, 0);
3069 }
3070 }
3071
3072 // Called by WebKit to instruct the UI to hide the keyboard
3073 private void hideSoftKeyboard() {
3074 InputMethodManager imm = (InputMethodManager)
3075 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
3076
3077 imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003078 }
3079
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003080 /*
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003081 * This method checks the current focus and cursor and potentially rebuilds
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003082 * mWebTextView to have the appropriate properties, such as password,
3083 * multiline, and what text it contains. It also removes it if necessary.
3084 */
3085 private void rebuildWebTextView() {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003086 // If the WebView does not have focus, do nothing until it gains focus.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003087 if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())
Cary Clarkd6982c92009-05-29 11:02:22 -04003088 || (mTouchMode >= FIRST_SCROLL_ZOOM
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003089 && mTouchMode <= LAST_SCROLL_ZOOM)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003090 return;
3091 }
3092 boolean alreadyThere = inEditingMode();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003093 // inEditingMode can only return true if mWebTextView is non-null,
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003094 // so we can safely call remove() if (alreadyThere)
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003095 if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003096 if (alreadyThere) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003097 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003098 }
3099 return;
3100 }
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003101 // At this point, we know we have found an input field, so go ahead
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003102 // and create the WebTextView if necessary.
3103 if (mWebTextView == null) {
3104 mWebTextView = new WebTextView(mContext, WebView.this);
Leon Scroggins2ca912e2009-04-28 16:51:55 -04003105 // Initialize our generation number.
3106 mTextGeneration = 0;
3107 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003108 mWebTextView.setTextSize(contentToView(nativeFocusCandidateTextSize()));
Cary Clark3524be92009-06-22 13:09:11 -04003109 Rect visibleRect = new Rect();
3110 calcOurContentVisibleRect(visibleRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003111 // Note that sendOurVisibleRect calls viewToContent, so the coordinates
3112 // should be in content coordinates.
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003113 Rect bounds = nativeFocusCandidateNodeBounds();
Cary Clarkd6982c92009-05-29 11:02:22 -04003114 if (!Rect.intersects(bounds, visibleRect)) {
Leon Scroggins40981262009-07-01 10:57:47 -04003115 mWebTextView.bringIntoView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003116 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003117 String text = nativeFocusCandidateText();
3118 int nodePointer = nativeFocusCandidatePointer();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003119 if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003120 // It is possible that we have the same textfield, but it has moved,
3121 // i.e. In the case of opening/closing the screen.
3122 // In that case, we need to set the dimensions, but not the other
3123 // aspects.
3124 // We also need to restore the selection, which gets wrecked by
3125 // calling setTextEntryRect.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003126 Spannable spannable = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003127 int start = Selection.getSelectionStart(spannable);
3128 int end = Selection.getSelectionEnd(spannable);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003129 // If the text has been changed by webkit, update it. However, if
3130 // there has been more UI text input, ignore it. We will receive
3131 // another update when that text is recognized.
Cary Clarkd6982c92009-05-29 11:02:22 -04003132 if (text != null && !text.equals(spannable.toString())
3133 && nativeTextGeneration() == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003134 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003135 } else {
3136 Selection.setSelection(spannable, start, end);
3137 }
3138 } else {
Cary Clarkd6982c92009-05-29 11:02:22 -04003139 Rect vBox = contentToView(bounds);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003140 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
3141 vBox.height());
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003142 mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
3143 Gravity.RIGHT : Gravity.NO_GRAVITY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003144 // this needs to be called before update adapter thread starts to
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003145 // ensure the mWebTextView has the same node pointer
3146 mWebTextView.setNodePointer(nodePointer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003147 int maxLength = -1;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003148 boolean isTextField = nativeFocusCandidateIsTextField();
Cary Clarkd6982c92009-05-29 11:02:22 -04003149 if (isTextField) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003150 maxLength = nativeFocusCandidateMaxLength();
3151 String name = nativeFocusCandidateName();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003152 if (mWebViewCore.getSettings().getSaveFormData()
Cary Clarkd6982c92009-05-29 11:02:22 -04003153 && name != null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003154 Message update = mPrivateHandler.obtainMessage(
Cary Clarkded054c2009-06-15 10:26:08 -04003155 REQUEST_FORM_DATA, nodePointer);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003156 RequestFormData updater = new RequestFormData(name,
3157 getUrl(), update);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003158 Thread t = new Thread(updater);
3159 t.start();
3160 }
3161 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003162 mWebTextView.setMaxLength(maxLength);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003163 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003164 mWebTextView.setAdapterCustom(adapter);
3165 mWebTextView.setSingleLine(isTextField);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003166 mWebTextView.setInPassword(nativeFocusCandidateIsPassword());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003167 if (null == text) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003168 mWebTextView.setText("", 0, 0);
Cary Clark243ea062009-06-25 10:49:32 -04003169 if (DebugFlags.WEB_VIEW) {
3170 Log.v(LOGTAG, "rebuildWebTextView null == text");
3171 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003172 } else {
3173 // Change to true to enable the old style behavior, where
3174 // entering a textfield/textarea always set the selection to the
3175 // whole field. This was desirable for the case where the user
3176 // intends to scroll past the field using the trackball.
3177 // However, it causes a problem when replying to emails - the
3178 // user expects the cursor to be at the beginning of the
3179 // textarea. Testing out a new behavior, where textfields set
3180 // selection at the end, and textareas at the beginning.
3181 if (false) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003182 mWebTextView.setText(text, 0, text.length());
Cary Clarkd6982c92009-05-29 11:02:22 -04003183 } else if (isTextField) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003184 int length = text.length();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003185 mWebTextView.setText(text, length, length);
Cary Clark243ea062009-06-25 10:49:32 -04003186 if (DebugFlags.WEB_VIEW) {
3187 Log.v(LOGTAG, "rebuildWebTextView length=" + length);
3188 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003189 } else {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003190 mWebTextView.setText(text, 0, 0);
Cary Clark243ea062009-06-25 10:49:32 -04003191 if (DebugFlags.WEB_VIEW) {
3192 Log.v(LOGTAG, "rebuildWebTextView !isTextField");
3193 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003194 }
3195 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003196 mWebTextView.requestFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003197 }
3198 }
3199
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003200 /*
3201 * This class requests an Adapter for the WebTextView which shows past
3202 * entries stored in the database. It is a Runnable so that it can be done
3203 * in its own thread, without slowing down the UI.
3204 */
3205 private class RequestFormData implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003206 private String mName;
3207 private String mUrl;
3208 private Message mUpdateMessage;
3209
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003210 public RequestFormData(String name, String url, Message msg) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003211 mName = name;
3212 mUrl = url;
3213 mUpdateMessage = msg;
3214 }
3215
3216 public void run() {
3217 ArrayList<String> pastEntries = mDatabase.getFormData(mUrl, mName);
3218 if (pastEntries.size() > 0) {
3219 AutoCompleteAdapter adapter = new
3220 AutoCompleteAdapter(mContext, pastEntries);
Cary Clarkded054c2009-06-15 10:26:08 -04003221 mUpdateMessage.obj = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003222 mUpdateMessage.sendToTarget();
3223 }
3224 }
3225 }
3226
Leon Scrogginse3225672009-06-03 15:53:13 -04003227 // This is used to determine long press with the center key. Does not
3228 // affect long press with the trackball/touch.
3229 private boolean mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003230
3231 @Override
3232 public boolean onKeyDown(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003233 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003234 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003235 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003236 }
3237
3238 if (mNativeClass == 0) {
3239 return false;
3240 }
3241
3242 // do this hack up front, so it always works, regardless of touch-mode
3243 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
3244 mAutoRedraw = !mAutoRedraw;
3245 if (mAutoRedraw) {
3246 invalidate();
3247 }
3248 return true;
3249 }
3250
3251 // Bubble up the key event if
3252 // 1. it is a system key; or
3253 // 2. the host application wants to handle it; or
3254 // 3. webview is in scroll-zoom state;
3255 if (event.isSystem()
3256 || mCallbackProxy.uiOverrideKeyEvent(event)
3257 || (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM)) {
3258 return false;
3259 }
3260
Cary Clark2f1d60c2009-06-03 08:05:53 -04003261 if (mShiftIsPressed == false && nativeCursorWantsKeyEvents() == false
Cary Clarkd6982c92009-05-29 11:02:22 -04003262 && (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003263 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT)) {
3264 mExtendSelection = false;
3265 mShiftIsPressed = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04003266 if (nativeHasCursorNode()) {
3267 Rect rect = nativeCursorNodeBounds();
3268 mSelectX = contentToView(rect.left);
3269 mSelectY = contentToView(rect.top);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003270 } else {
3271 mSelectX = mScrollX + (int) mLastTouchX;
3272 mSelectY = mScrollY + (int) mLastTouchY;
3273 }
Cary Clarke872f3a2009-06-11 09:51:11 -04003274 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003275 }
3276
3277 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3278 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3279 // always handle the navigation keys in the UI thread
3280 switchOutDrawHistory();
Cary Clark215b72c2009-06-26 14:38:43 -04003281 if (navHandledKey(keyCode, 1, false, event.getEventTime(), false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003282 playSoundEffect(keyCodeToSoundsEffect(keyCode));
3283 return true;
3284 }
3285 // Bubble up the key event as WebView doesn't handle it
3286 return false;
3287 }
3288
Leon Scrogginse3225672009-06-03 15:53:13 -04003289 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003290 switchOutDrawHistory();
3291 if (event.getRepeatCount() == 0) {
Leon Scrogginse3225672009-06-03 15:53:13 -04003292 mGotCenterDown = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003293 mPrivateHandler.sendMessageDelayed(mPrivateHandler
Leon Scrogginse3225672009-06-03 15:53:13 -04003294 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003295 // Already checked mNativeClass, so we do not need to check it
3296 // again.
3297 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
3298 return true;
3299 }
3300 // Bubble up the key event as WebView doesn't handle it
3301 return false;
3302 }
3303
Cary Clark843bbb82009-04-20 16:03:31 -04003304 if (keyCode != KeyEvent.KEYCODE_SHIFT_LEFT
3305 && keyCode != KeyEvent.KEYCODE_SHIFT_RIGHT) {
3306 // turn off copy select if a shift-key combo is pressed
3307 mExtendSelection = mShiftIsPressed = false;
3308 if (mTouchMode == TOUCH_SELECT_MODE) {
3309 mTouchMode = TOUCH_INIT_MODE;
3310 }
3311 }
3312
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003313 if (getSettings().getNavDump()) {
3314 switch (keyCode) {
3315 case KeyEvent.KEYCODE_4:
3316 // "/data/data/com.android.browser/displayTree.txt"
3317 nativeDumpDisplayTree(getUrl());
3318 break;
3319 case KeyEvent.KEYCODE_5:
3320 case KeyEvent.KEYCODE_6:
3321 // 5: dump the dom tree to the file
3322 // "/data/data/com.android.browser/domTree.txt"
3323 // 6: dump the dom tree to the adb log
3324 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE,
3325 (keyCode == KeyEvent.KEYCODE_5) ? 1 : 0, 0);
3326 break;
3327 case KeyEvent.KEYCODE_7:
3328 case KeyEvent.KEYCODE_8:
3329 // 7: dump the render tree to the file
3330 // "/data/data/com.android.browser/renderTree.txt"
3331 // 8: dump the render tree to the adb log
3332 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE,
3333 (keyCode == KeyEvent.KEYCODE_7) ? 1 : 0, 0);
3334 break;
3335 case KeyEvent.KEYCODE_9:
3336 nativeInstrumentReport();
3337 return true;
3338 }
3339 }
3340
Cary Clark215b72c2009-06-26 14:38:43 -04003341 if (nativeCursorIsPlugin()) {
3342 nativeUpdatePluginReceivesEvents();
3343 invalidate();
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003344 } else if (nativeCursorIsTextInput()) {
Leon Scroggins1cc24202009-06-16 10:10:28 -04003345 // This message will put the node in focus, for the DOM's notion
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003346 // of focus, and make the focuscontroller active
Cary Clark19436562009-06-04 16:25:07 -04003347 mWebViewCore.sendMessage(EventHub.CLICK);
Leon Scroggins0658e8f2009-06-26 14:09:09 -04003348 // This will bring up the WebTextView and put it in focus, for
3349 // our view system's notion of focus
3350 rebuildWebTextView();
3351 // Now we need to pass the event to it
3352 return mWebTextView.onKeyDown(keyCode, event);
Leon Scroggins40981262009-07-01 10:57:47 -04003353 } else if (nativeHasFocusNode()) {
3354 // In this case, the cursor is not on a text input, but the focus
3355 // might be. Check it, and if so, hand over to the WebTextView.
3356 rebuildWebTextView();
3357 if (inEditingMode()) {
3358 return mWebTextView.onKeyDown(keyCode, event);
3359 }
Cary Clark19436562009-06-04 16:25:07 -04003360 }
3361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003362 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003363 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003364 // pass the key to DOM
3365 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
3366 // return true as DOM handles the key
3367 return true;
3368 }
3369
3370 // Bubble up the key event as WebView doesn't handle it
3371 return false;
3372 }
3373
3374 @Override
3375 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003376 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003377 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04003378 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003379 }
3380
3381 if (mNativeClass == 0) {
3382 return false;
3383 }
3384
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04003385 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04003386 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
3387 String text = nativeCursorText();
3388 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003389 && text.startsWith(SCHEME_TEL)) {
3390 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
3391 getContext().startActivity(intent);
3392 return true;
3393 }
3394 }
3395
3396 // Bubble up the key event if
3397 // 1. it is a system key; or
3398 // 2. the host application wants to handle it;
3399 if (event.isSystem() || mCallbackProxy.uiOverrideKeyEvent(event)) {
3400 return false;
3401 }
3402
3403 // special handling in scroll_zoom state
3404 if (mTouchMode >= FIRST_SCROLL_ZOOM && mTouchMode <= LAST_SCROLL_ZOOM) {
3405 if (KeyEvent.KEYCODE_DPAD_CENTER == keyCode
3406 && mTouchMode != SCROLL_ZOOM_ANIMATION_IN) {
3407 setZoomScrollIn();
3408 mTouchMode = SCROLL_ZOOM_ANIMATION_IN;
3409 invalidate();
3410 return true;
3411 }
3412 return false;
3413 }
3414
Cary Clarkd6982c92009-05-29 11:02:22 -04003415 if (keyCode == KeyEvent.KEYCODE_SHIFT_LEFT
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003416 || keyCode == KeyEvent.KEYCODE_SHIFT_RIGHT) {
3417 if (commitCopy()) {
3418 return true;
3419 }
3420 }
3421
3422 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
3423 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
3424 // always handle the navigation keys in the UI thread
3425 // Bubble up the key event as WebView doesn't handle it
3426 return false;
3427 }
3428
Leon Scrogginse3225672009-06-03 15:53:13 -04003429 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003430 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04003431 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
3432 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003433
Leon Scrogginse3225672009-06-03 15:53:13 -04003434 if (mShiftIsPressed) {
3435 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003436 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003437 if (getSettings().supportZoom()
3438 && mTouchMode == TOUCH_DOUBLECLICK_MODE) {
3439 zoomScrollOut();
3440 } else {
3441 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3442 .obtainMessage(SWITCH_TO_CLICK), TAP_TIMEOUT);
3443 if (DebugFlags.WEB_VIEW) {
3444 Log.v(LOGTAG, "TOUCH_DOUBLECLICK_MODE");
3445 }
3446 mTouchMode = TOUCH_DOUBLECLICK_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003447 }
Leon Scrogginse3225672009-06-03 15:53:13 -04003448 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003449 }
3450
3451 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04003452 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003453 // pass the key to DOM
3454 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
3455 // return true as DOM handles the key
3456 return true;
3457 }
3458
3459 // Bubble up the key event as WebView doesn't handle it
3460 return false;
3461 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003462
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003463 /**
3464 * @hide
3465 */
3466 public void emulateShiftHeld() {
3467 mExtendSelection = false;
3468 mShiftIsPressed = true;
Cary Clarke872f3a2009-06-11 09:51:11 -04003469 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003470 }
3471
3472 private boolean commitCopy() {
3473 boolean copiedSomething = false;
3474 if (mExtendSelection) {
3475 // copy region so core operates on copy without touching orig.
3476 Region selection = new Region(nativeGetSelection());
3477 if (selection.isEmpty() == false) {
3478 Toast.makeText(mContext
3479 , com.android.internal.R.string.text_copied
3480 , Toast.LENGTH_SHORT).show();
3481 mWebViewCore.sendMessage(EventHub.GET_SELECTION, selection);
3482 copiedSomething = true;
3483 }
3484 mExtendSelection = false;
3485 }
3486 mShiftIsPressed = false;
3487 if (mTouchMode == TOUCH_SELECT_MODE) {
3488 mTouchMode = TOUCH_INIT_MODE;
3489 }
3490 return copiedSomething;
3491 }
3492
3493 // Set this as a hierarchy change listener so we can know when this view
3494 // is removed and still have access to our parent.
3495 @Override
3496 protected void onAttachedToWindow() {
3497 super.onAttachedToWindow();
3498 ViewParent parent = getParent();
3499 if (parent instanceof ViewGroup) {
3500 ViewGroup p = (ViewGroup) parent;
3501 p.setOnHierarchyChangeListener(this);
3502 }
3503 }
3504
3505 @Override
3506 protected void onDetachedFromWindow() {
3507 super.onDetachedFromWindow();
3508 ViewParent parent = getParent();
3509 if (parent instanceof ViewGroup) {
3510 ViewGroup p = (ViewGroup) parent;
3511 p.setOnHierarchyChangeListener(null);
3512 }
3513
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003514 // Clean up the zoom controller
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003515 mZoomButtonsController.setVisible(false);
3516 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003517
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003518 // Implementation for OnHierarchyChangeListener
3519 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04003520
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003521 public void onChildViewRemoved(View p, View child) {
3522 if (child == this) {
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003523 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003524 }
3525 }
3526
3527 /**
3528 * @deprecated WebView should not have implemented
3529 * ViewTreeObserver.OnGlobalFocusChangeListener. This method
3530 * does nothing now.
3531 */
3532 @Deprecated
3533 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
3534 }
3535
Cary Clarkd6982c92009-05-29 11:02:22 -04003536 // To avoid drawing the cursor ring, and remove the TextView when our window
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003537 // loses focus.
3538 @Override
3539 public void onWindowFocusChanged(boolean hasWindowFocus) {
3540 if (hasWindowFocus) {
3541 if (hasFocus()) {
3542 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003543 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04003544 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003545 if (mNativeClass != 0) {
3546 nativeRecordButtons(true, false, true);
Leon Scroggins8cdad882009-06-30 08:47:04 -04003547 if (inEditingMode()) {
3548 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 1, 0);
3549 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003550 }
3551 } else {
3552 // If our window gained focus, but we do not have it, do not
Cary Clarkd6982c92009-05-29 11:02:22 -04003553 // draw the cursor ring.
3554 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003555 // We do not call nativeRecordButtons here because we assume
3556 // that when we lost focus, or window focus, it got called with
3557 // false for the first parameter
3558 }
3559 } else {
The Android Open Source Project10592532009-03-18 17:39:46 -07003560 if (getSettings().getBuiltInZoomControls() && !mZoomButtonsController.isVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003561 /*
3562 * The zoom controls come in their own window, so our window
Cary Clarkd6982c92009-05-29 11:02:22 -04003563 * loses focus. Our policy is to not draw the cursor ring if
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003564 * our window is not focused, but this is an exception since
3565 * the user can still navigate the web page with the zoom
3566 * controls showing.
3567 */
Cary Clarkd6982c92009-05-29 11:02:22 -04003568 // If our window has lost focus, stop drawing the cursor ring
3569 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003570 }
3571 mGotKeyDown = false;
3572 mShiftIsPressed = false;
3573 if (mNativeClass != 0) {
3574 nativeRecordButtons(false, false, true);
3575 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003576 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003577 }
3578 invalidate();
3579 super.onWindowFocusChanged(hasWindowFocus);
3580 }
3581
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003582 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003583 * Pass a message to WebCore Thread, telling the WebCore::Page's
3584 * FocusController to be "inactive" so that it will
3585 * not draw the blinking cursor. It gets set to "active" to draw the cursor
3586 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003587 */
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003588 private void setFocusControllerInactive() {
Leon Scrogginsfd06bc82009-06-08 13:22:02 -04003589 // Do not need to also check whether mWebViewCore is null, because
3590 // mNativeClass is only set if mWebViewCore is non null
3591 if (mNativeClass == 0) return;
Leon Scroggins8cdad882009-06-30 08:47:04 -04003592 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, 0, 0);
Leon Scroggins63dda1c2009-04-15 13:25:00 -04003593 }
3594
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003595 @Override
3596 protected void onFocusChanged(boolean focused, int direction,
3597 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003598 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003599 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
3600 }
3601 if (focused) {
3602 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003603 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003604 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003605 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003606 if (mNativeClass != 0) {
3607 nativeRecordButtons(true, false, true);
3608 }
3609 //} else {
3610 // The WebView has gained focus while we do not have
3611 // windowfocus. When our window lost focus, we should have
3612 // called nativeRecordButtons(false...)
3613 }
3614 } else {
3615 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04003616 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003617 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04003618 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003619 if (mNativeClass != 0) {
3620 nativeRecordButtons(false, false, true);
3621 }
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04003622 setFocusControllerInactive();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003623 }
3624 mGotKeyDown = false;
3625 }
3626
3627 super.onFocusChanged(focused, direction, previouslyFocusedRect);
3628 }
3629
3630 @Override
3631 protected void onSizeChanged(int w, int h, int ow, int oh) {
3632 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003633 // Center zooming to the center of the screen.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003634 mZoomCenterX = getViewWidth() * .5f;
3635 mZoomCenterY = getViewHeight() * .5f;
3636
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003637 // update mMinZoomScale if the minimum zoom scale is not fixed
3638 if (!mMinZoomScaleFixed) {
3639 mMinZoomScale = (float) getViewWidth()
3640 / Math.max(ZOOM_OUT_WIDTH, mContentWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003641 }
3642
3643 // we always force, in case our height changed, in which case we still
3644 // want to send the notification over to webkit
3645 setNewZoomScale(mActualScale, true);
3646 }
3647
3648 @Override
3649 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
3650 super.onScrollChanged(l, t, oldl, oldt);
Patrick Scott0a5ce012009-07-02 08:56:10 -04003651
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003652 sendOurVisibleRect();
3653 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003654
3655
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003656 @Override
3657 public boolean dispatchKeyEvent(KeyEvent event) {
3658 boolean dispatch = true;
3659
3660 if (!inEditingMode()) {
3661 if (event.getAction() == KeyEvent.ACTION_DOWN) {
3662 mGotKeyDown = true;
3663 } else {
3664 if (!mGotKeyDown) {
3665 /*
3666 * We got a key up for which we were not the recipient of
3667 * the original key down. Don't give it to the view.
3668 */
3669 dispatch = false;
3670 }
3671 mGotKeyDown = false;
3672 }
3673 }
3674
3675 if (dispatch) {
3676 return super.dispatchKeyEvent(event);
3677 } else {
3678 // We didn't dispatch, so let something else handle the key
3679 return false;
3680 }
3681 }
3682
3683 // Here are the snap align logic:
3684 // 1. If it starts nearly horizontally or vertically, snap align;
3685 // 2. If there is a dramitic direction change, let it go;
3686 // 3. If there is a same direction back and forth, lock it.
3687
3688 // adjustable parameters
3689 private int mMinLockSnapReverseDistance;
3690 private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
3691 private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
3692
3693 @Override
3694 public boolean onTouchEvent(MotionEvent ev) {
3695 if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
3696 return false;
3697 }
3698
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003699 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003700 Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
3701 + mTouchMode);
3702 }
3703
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003704 int action = ev.getAction();
3705 float x = ev.getX();
3706 float y = ev.getY();
3707 long eventTime = ev.getEventTime();
3708
3709 // Due to the touch screen edge effect, a touch closer to the edge
3710 // always snapped to the edge. As getViewWidth() can be different from
3711 // getWidth() due to the scrollbar, adjusting the point to match
3712 // getViewWidth(). Same applied to the height.
3713 if (x > getViewWidth() - 1) {
3714 x = getViewWidth() - 1;
3715 }
3716 if (y > getViewHeight() - 1) {
3717 y = getViewHeight() - 1;
3718 }
3719
3720 // pass the touch events from UI thread to WebCore thread
3721 if (mForwardTouchEvents && mTouchMode != SCROLL_ZOOM_OUT
3722 && mTouchMode != SCROLL_ZOOM_ANIMATION_IN
3723 && mTouchMode != SCROLL_ZOOM_ANIMATION_OUT
Cary Clarkd6982c92009-05-29 11:02:22 -04003724 && (action != MotionEvent.ACTION_MOVE ||
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003725 eventTime - mLastSentTouchTime > TOUCH_SENT_INTERVAL)) {
3726 WebViewCore.TouchEventData ted = new WebViewCore.TouchEventData();
3727 ted.mAction = action;
3728 ted.mX = viewToContent((int) x + mScrollX);
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003729 ted.mY = viewToContent((int) y + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003730 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
3731 mLastSentTouchTime = eventTime;
3732 }
3733
3734 int deltaX = (int) (mLastTouchX - x);
3735 int deltaY = (int) (mLastTouchY - y);
3736
3737 switch (action) {
3738 case MotionEvent.ACTION_DOWN: {
3739 if (mTouchMode == SCROLL_ZOOM_ANIMATION_IN
3740 || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
3741 // no interaction while animation is in progress
3742 break;
3743 } else if (mTouchMode == SCROLL_ZOOM_OUT) {
3744 mLastScrollX = mZoomScrollX;
3745 mLastScrollY = mZoomScrollY;
3746 // If two taps are close, ignore the first tap
3747 } else if (!mScroller.isFinished()) {
3748 mScroller.abortAnimation();
3749 mTouchMode = TOUCH_DRAG_START_MODE;
3750 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
3751 } else if (mShiftIsPressed) {
3752 mSelectX = mScrollX + (int) x;
3753 mSelectY = mScrollY + (int) y;
3754 mTouchMode = TOUCH_SELECT_MODE;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003755 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003756 Log.v(LOGTAG, "select=" + mSelectX + "," + mSelectY);
3757 }
3758 nativeMoveSelection(viewToContent(mSelectX)
3759 , viewToContent(mSelectY), false);
3760 mTouchSelection = mExtendSelection = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003761 } else {
3762 mTouchMode = TOUCH_INIT_MODE;
3763 mPreventDrag = mForwardTouchEvents;
3764 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
3765 EventLog.writeEvent(EVENT_LOG_DOUBLE_TAP_DURATION,
3766 (eventTime - mLastTouchUpTime), eventTime);
3767 }
3768 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07003769 // Trigger the link
3770 if (mTouchMode == TOUCH_INIT_MODE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003771 mPrivateHandler.sendMessageDelayed(mPrivateHandler
3772 .obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
3773 }
3774 // Remember where the motion event started
3775 mLastTouchX = x;
3776 mLastTouchY = y;
3777 mLastTouchTime = eventTime;
3778 mVelocityTracker = VelocityTracker.obtain();
3779 mSnapScrollMode = SNAP_NONE;
3780 break;
3781 }
3782 case MotionEvent.ACTION_MOVE: {
Cary Clarkd6982c92009-05-29 11:02:22 -04003783 if (mTouchMode == TOUCH_DONE_MODE
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003784 || mTouchMode == SCROLL_ZOOM_ANIMATION_IN
3785 || mTouchMode == SCROLL_ZOOM_ANIMATION_OUT) {
3786 // no dragging during scroll zoom animation
3787 break;
3788 }
3789 if (mTouchMode == SCROLL_ZOOM_OUT) {
3790 // while fully zoomed out, move the virtual window
3791 moveZoomScrollWindow(x, y);
3792 break;
3793 }
3794 mVelocityTracker.addMovement(ev);
3795
3796 if (mTouchMode != TOUCH_DRAG_MODE) {
3797 if (mTouchMode == TOUCH_SELECT_MODE) {
3798 mSelectX = mScrollX + (int) x;
3799 mSelectY = mScrollY + (int) y;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003800 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003801 Log.v(LOGTAG, "xtend=" + mSelectX + "," + mSelectY);
3802 }
3803 nativeMoveSelection(viewToContent(mSelectX)
3804 , viewToContent(mSelectY), true);
3805 invalidate();
3806 break;
3807 }
3808 if (mPreventDrag || (deltaX * deltaX + deltaY * deltaY)
3809 < mTouchSlopSquare) {
3810 break;
3811 }
3812
3813 if (mTouchMode == TOUCH_SHORTPRESS_MODE
3814 || mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
3815 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
3816 } else if (mTouchMode == TOUCH_INIT_MODE) {
3817 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
3818 }
3819
3820 // if it starts nearly horizontal or vertical, enforce it
3821 int ax = Math.abs(deltaX);
3822 int ay = Math.abs(deltaY);
3823 if (ax > MAX_SLOPE_FOR_DIAG * ay) {
3824 mSnapScrollMode = SNAP_X;
3825 mSnapPositive = deltaX > 0;
3826 } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
3827 mSnapScrollMode = SNAP_Y;
3828 mSnapPositive = deltaY > 0;
3829 }
3830
3831 mTouchMode = TOUCH_DRAG_MODE;
3832 WebViewCore.pauseUpdate(mWebViewCore);
Cary Clarke872f3a2009-06-11 09:51:11 -04003833 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003834 // remove the zoom anchor if there is any
3835 if (mZoomScale != 0) {
3836 mWebViewCore
3837 .sendMessage(EventHub.SET_SNAP_ANCHOR, 0, 0);
3838 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003839 WebSettings settings = getSettings();
3840 if (settings.supportZoom()
3841 && settings.getBuiltInZoomControls()
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003842 && !mZoomButtonsController.isVisible()
Cary Clarkd6982c92009-05-29 11:02:22 -04003843 && (canZoomScrollOut() ||
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003844 mMinZoomScale < mMaxZoomScale)) {
3845 mZoomButtonsController.setVisible(true);
3846 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003847 }
3848
3849 // do pan
3850 int newScrollX = pinLocX(mScrollX + deltaX);
3851 deltaX = newScrollX - mScrollX;
3852 int newScrollY = pinLocY(mScrollY + deltaY);
3853 deltaY = newScrollY - mScrollY;
3854 boolean done = false;
3855 if (deltaX == 0 && deltaY == 0) {
3856 done = true;
3857 } else {
3858 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
3859 int ax = Math.abs(deltaX);
3860 int ay = Math.abs(deltaY);
3861 if (mSnapScrollMode == SNAP_X) {
3862 // radical change means getting out of snap mode
3863 if (ay > MAX_SLOPE_FOR_DIAG * ax
3864 && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
3865 mSnapScrollMode = SNAP_NONE;
3866 }
3867 // reverse direction means lock in the snap mode
3868 if ((ax > MAX_SLOPE_FOR_DIAG * ay) &&
Cary Clarkd6982c92009-05-29 11:02:22 -04003869 ((mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003870 deltaX < -mMinLockSnapReverseDistance)
3871 || (!mSnapPositive &&
3872 deltaX > mMinLockSnapReverseDistance))) {
3873 mSnapScrollMode = SNAP_X_LOCK;
3874 }
3875 } else {
3876 // radical change means getting out of snap mode
3877 if ((ax > MAX_SLOPE_FOR_DIAG * ay)
3878 && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
3879 mSnapScrollMode = SNAP_NONE;
3880 }
3881 // reverse direction means lock in the snap mode
3882 if ((ay > MAX_SLOPE_FOR_DIAG * ax) &&
Cary Clarkd6982c92009-05-29 11:02:22 -04003883 ((mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003884 deltaY < -mMinLockSnapReverseDistance)
Cary Clarkd6982c92009-05-29 11:02:22 -04003885 || (!mSnapPositive &&
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003886 deltaY > mMinLockSnapReverseDistance))) {
3887 mSnapScrollMode = SNAP_Y_LOCK;
3888 }
3889 }
3890 }
3891
3892 if (mSnapScrollMode == SNAP_X
3893 || mSnapScrollMode == SNAP_X_LOCK) {
3894 scrollBy(deltaX, 0);
3895 mLastTouchX = x;
3896 } else if (mSnapScrollMode == SNAP_Y
3897 || mSnapScrollMode == SNAP_Y_LOCK) {
3898 scrollBy(0, deltaY);
3899 mLastTouchY = y;
3900 } else {
3901 scrollBy(deltaX, deltaY);
3902 mLastTouchX = x;
3903 mLastTouchY = y;
3904 }
3905 mLastTouchTime = eventTime;
3906 mUserScroll = true;
3907 }
The Android Open Source Project10592532009-03-18 17:39:46 -07003908
3909 if (!getSettings().getBuiltInZoomControls()) {
3910 boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
3911 boolean showMagnify = canZoomScrollOut();
3912 if (mZoomControls != null && (showPlusMinus || showMagnify)) {
3913 if (mZoomControls.getVisibility() == View.VISIBLE) {
3914 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
3915 } else {
3916 mZoomControls.show(showPlusMinus, showMagnify);
3917 }
3918 mPrivateHandler.postDelayed(mZoomControlRunnable,
3919 ZOOM_CONTROLS_TIMEOUT);
3920 }
3921 }
3922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003923 if (done) {
3924 // return false to indicate that we can't pan out of the
3925 // view space
3926 return false;
3927 }
3928 break;
3929 }
3930 case MotionEvent.ACTION_UP: {
3931 mLastTouchUpTime = eventTime;
3932 switch (mTouchMode) {
3933 case TOUCH_INIT_MODE: // tap
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003934 case TOUCH_SHORTPRESS_START_MODE:
3935 case TOUCH_SHORTPRESS_MODE:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003936 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07003937 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
3938 mTouchMode = TOUCH_DONE_MODE;
3939 doShortPress();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003940 break;
3941 case TOUCH_SELECT_MODE:
3942 commitCopy();
3943 mTouchSelection = false;
3944 break;
3945 case SCROLL_ZOOM_ANIMATION_IN:
3946 case SCROLL_ZOOM_ANIMATION_OUT:
3947 // no action during scroll animation
3948 break;
3949 case SCROLL_ZOOM_OUT:
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04003950 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003951 Log.v(LOGTAG, "ACTION_UP SCROLL_ZOOM_OUT"
3952 + " eventTime - mLastTouchTime="
3953 + (eventTime - mLastTouchTime));
3954 }
3955 // for now, always zoom back when the drag completes
3956 if (true || eventTime - mLastTouchTime < TAP_TIMEOUT) {
3957 // but if we tap, zoom in where we tap
3958 if (eventTime - mLastTouchTime < TAP_TIMEOUT) {
3959 zoomScrollTap(x, y);
3960 }
3961 // start zooming in back to the original view
3962 setZoomScrollIn();
3963 mTouchMode = SCROLL_ZOOM_ANIMATION_IN;
3964 invalidate();
3965 }
3966 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003967 case TOUCH_DRAG_MODE:
3968 // if the user waits a while w/o moving before the
3969 // up, we don't want to do a fling
3970 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
3971 mVelocityTracker.addMovement(ev);
3972 doFling();
3973 break;
3974 }
3975 WebViewCore.resumeUpdate(mWebViewCore);
3976 break;
3977 case TOUCH_DRAG_START_MODE:
3978 case TOUCH_DONE_MODE:
3979 // do nothing
3980 break;
3981 }
3982 // we also use mVelocityTracker == null to tell us that we are
3983 // not "moving around", so we can take the slower/prettier
3984 // mode in the drawing code
3985 if (mVelocityTracker != null) {
3986 mVelocityTracker.recycle();
3987 mVelocityTracker = null;
3988 }
3989 break;
3990 }
3991 case MotionEvent.ACTION_CANCEL: {
3992 // we also use mVelocityTracker == null to tell us that we are
3993 // not "moving around", so we can take the slower/prettier
3994 // mode in the drawing code
3995 if (mVelocityTracker != null) {
3996 mVelocityTracker.recycle();
3997 mVelocityTracker = null;
3998 }
3999 if (mTouchMode == SCROLL_ZOOM_OUT ||
4000 mTouchMode == SCROLL_ZOOM_ANIMATION_IN) {
4001 scrollTo(mZoomScrollX, mZoomScrollY);
4002 } else if (mTouchMode == TOUCH_DRAG_MODE) {
4003 WebViewCore.resumeUpdate(mWebViewCore);
4004 }
4005 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
4006 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004007 mTouchMode = TOUCH_DONE_MODE;
Cary Clarke872f3a2009-06-11 09:51:11 -04004008 nativeHideCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004009 break;
4010 }
4011 }
4012 return true;
4013 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004014
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004015 private long mTrackballFirstTime = 0;
4016 private long mTrackballLastTime = 0;
4017 private float mTrackballRemainsX = 0.0f;
4018 private float mTrackballRemainsY = 0.0f;
4019 private int mTrackballXMove = 0;
4020 private int mTrackballYMove = 0;
4021 private boolean mExtendSelection = false;
4022 private boolean mTouchSelection = false;
4023 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
4024 private static final int TRACKBALL_TIMEOUT = 200;
4025 private static final int TRACKBALL_WAIT = 100;
4026 private static final int TRACKBALL_SCALE = 400;
4027 private static final int TRACKBALL_SCROLL_COUNT = 5;
4028 private static final int TRACKBALL_MOVE_COUNT = 10;
4029 private static final int TRACKBALL_MULTIPLIER = 3;
4030 private static final int SELECT_CURSOR_OFFSET = 16;
4031 private int mSelectX = 0;
4032 private int mSelectY = 0;
4033 private boolean mShiftIsPressed = false;
4034 private boolean mTrackballDown = false;
4035 private long mTrackballUpTime = 0;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004036 private long mLastCursorTime = 0;
4037 private Rect mLastCursorBounds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004038
4039 // Set by default; BrowserActivity clears to interpret trackball data
Cary Clarkd6982c92009-05-29 11:02:22 -04004040 // directly for movement. Currently, the framework only passes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004041 // arrow key events, not trackball events, from one child to the next
4042 private boolean mMapTrackballToArrowKeys = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004043
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004044 public void setMapTrackballToArrowKeys(boolean setMap) {
4045 mMapTrackballToArrowKeys = setMap;
4046 }
4047
4048 void resetTrackballTime() {
4049 mTrackballLastTime = 0;
4050 }
4051
4052 @Override
4053 public boolean onTrackballEvent(MotionEvent ev) {
4054 long time = ev.getEventTime();
4055 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
4056 if (ev.getY() > 0) pageDown(true);
4057 if (ev.getY() < 0) pageUp(true);
4058 return true;
4059 }
4060 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004061 mPrivateHandler.removeMessages(SWITCH_TO_CLICK);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004062 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004063 if (mNativeClass == 0) {
4064 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004065 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04004066 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004067 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
4068 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
4069 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004070 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004071 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004072 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004073 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004074 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004075 }
4076 if (isInTouchMode()) requestFocusFromTouch();
4077 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04004078 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004079 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04004080 // LONG_PRESS_CENTER is set in common onKeyDown
4081 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004082 mTrackballDown = false;
4083 mTrackballUpTime = time;
4084 if (mShiftIsPressed) {
4085 if (mExtendSelection) {
4086 commitCopy();
4087 } else {
4088 mExtendSelection = true;
4089 }
4090 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004091 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004092 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04004093 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004094 );
4095 }
4096 return false; // let common code in onKeyUp at it
4097 }
4098 if (mMapTrackballToArrowKeys && mShiftIsPressed == false) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004099 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004100 return false;
4101 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004102 // no move if we're still waiting on SWITCH_TO_CLICK timeout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004103 if (mTouchMode == TOUCH_DOUBLECLICK_MODE) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004104 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent 2 click quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004105 return true;
4106 }
4107 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004108 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004109 return true; // discard move if trackball is down
4110 }
4111 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004112 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004113 return true;
4114 }
4115 // TODO: alternatively we can do panning as touch does
4116 switchOutDrawHistory();
4117 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004118 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004119 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004120 + time + " last=" + mTrackballLastTime);
4121 }
4122 mTrackballFirstTime = time;
4123 mTrackballXMove = mTrackballYMove = 0;
4124 }
4125 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004126 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004127 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
4128 }
4129 mTrackballRemainsX += ev.getX();
4130 mTrackballRemainsY += ev.getY();
4131 doTrackball(time);
4132 return true;
4133 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004134
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004135 void moveSelection(float xRate, float yRate) {
4136 if (mNativeClass == 0)
4137 return;
4138 int width = getViewWidth();
4139 int height = getViewHeight();
4140 mSelectX += scaleTrackballX(xRate, width);
4141 mSelectY += scaleTrackballY(yRate, height);
4142 int maxX = width + mScrollX;
4143 int maxY = height + mScrollY;
4144 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
4145 , mSelectX));
4146 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
4147 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004148 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004149 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004150 + " mSelectX=" + mSelectX
4151 + " mSelectY=" + mSelectY
4152 + " mScrollX=" + mScrollX
4153 + " mScrollY=" + mScrollY
4154 + " xRate=" + xRate
4155 + " yRate=" + yRate
4156 );
4157 }
4158 nativeMoveSelection(viewToContent(mSelectX)
4159 , viewToContent(mSelectY), mExtendSelection);
4160 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004161 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004162 : 0;
4163 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04004164 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004165 : 0;
4166 pinScrollBy(scrollX, scrollY, true, 0);
4167 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
4168 requestRectangleOnScreen(select);
4169 invalidate();
4170 }
4171
4172 private int scaleTrackballX(float xRate, int width) {
4173 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
4174 int nextXMove = xMove;
4175 if (xMove > 0) {
4176 if (xMove > mTrackballXMove) {
4177 xMove -= mTrackballXMove;
4178 }
4179 } else if (xMove < mTrackballXMove) {
4180 xMove -= mTrackballXMove;
4181 }
4182 mTrackballXMove = nextXMove;
4183 return xMove;
4184 }
4185
4186 private int scaleTrackballY(float yRate, int height) {
4187 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
4188 int nextYMove = yMove;
4189 if (yMove > 0) {
4190 if (yMove > mTrackballYMove) {
4191 yMove -= mTrackballYMove;
4192 }
4193 } else if (yMove < mTrackballYMove) {
4194 yMove -= mTrackballYMove;
4195 }
4196 mTrackballYMove = nextYMove;
4197 return yMove;
4198 }
4199
4200 private int keyCodeToSoundsEffect(int keyCode) {
4201 switch(keyCode) {
4202 case KeyEvent.KEYCODE_DPAD_UP:
4203 return SoundEffectConstants.NAVIGATION_UP;
4204 case KeyEvent.KEYCODE_DPAD_RIGHT:
4205 return SoundEffectConstants.NAVIGATION_RIGHT;
4206 case KeyEvent.KEYCODE_DPAD_DOWN:
4207 return SoundEffectConstants.NAVIGATION_DOWN;
4208 case KeyEvent.KEYCODE_DPAD_LEFT:
4209 return SoundEffectConstants.NAVIGATION_LEFT;
4210 }
4211 throw new IllegalArgumentException("keyCode must be one of " +
4212 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
4213 "KEYCODE_DPAD_LEFT}.");
4214 }
4215
4216 private void doTrackball(long time) {
4217 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
4218 if (elapsed == 0) {
4219 elapsed = TRACKBALL_TIMEOUT;
4220 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004221 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004222 float yRate = mTrackballRemainsY * 1000 / elapsed;
4223 if (mShiftIsPressed) {
4224 moveSelection(xRate, yRate);
4225 mTrackballRemainsX = mTrackballRemainsY = 0;
4226 return;
4227 }
4228 float ax = Math.abs(xRate);
4229 float ay = Math.abs(yRate);
4230 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004231 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004232 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
4233 + " xRate=" + xRate
4234 + " yRate=" + yRate
4235 + " mTrackballRemainsX=" + mTrackballRemainsX
4236 + " mTrackballRemainsY=" + mTrackballRemainsY);
4237 }
4238 int width = mContentWidth - getViewWidth();
4239 int height = mContentHeight - getViewHeight();
4240 if (width < 0) width = 0;
4241 if (height < 0) height = 0;
4242 if (mTouchMode == SCROLL_ZOOM_OUT) {
4243 int oldX = mZoomScrollX;
4244 int oldY = mZoomScrollY;
4245 int maxWH = Math.max(width, height);
4246 mZoomScrollX += scaleTrackballX(xRate, maxWH);
4247 mZoomScrollY += scaleTrackballY(yRate, maxWH);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004248 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004249 Log.v(LOGTAG, "doTrackball SCROLL_ZOOM_OUT"
Cary Clarkd6982c92009-05-29 11:02:22 -04004250 + " mZoomScrollX=" + mZoomScrollX
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004251 + " mZoomScrollY=" + mZoomScrollY);
4252 }
4253 mZoomScrollX = Math.min(width, Math.max(0, mZoomScrollX));
4254 mZoomScrollY = Math.min(height, Math.max(0, mZoomScrollY));
4255 if (oldX != mZoomScrollX || oldY != mZoomScrollY) {
4256 invalidate();
4257 }
4258 mTrackballRemainsX = mTrackballRemainsY = 0;
4259 return;
4260 }
4261 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
4262 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
4263 maxA = Math.max(ax, ay);
4264 int count = Math.max(0, (int) maxA);
4265 int oldScrollX = mScrollX;
4266 int oldScrollY = mScrollY;
4267 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004268 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
4269 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004270 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
4271 KeyEvent.KEYCODE_DPAD_RIGHT;
4272 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004273 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004274 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004275 + " count=" + count
4276 + " mTrackballRemainsX=" + mTrackballRemainsX
4277 + " mTrackballRemainsY=" + mTrackballRemainsY);
4278 }
Cary Clark215b72c2009-06-26 14:38:43 -04004279 if (navHandledKey(selectKeyCode, count, false, time, false)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004280 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
4281 }
4282 mTrackballRemainsX = mTrackballRemainsY = 0;
4283 }
4284 if (count >= TRACKBALL_SCROLL_COUNT) {
4285 int xMove = scaleTrackballX(xRate, width);
4286 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004287 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004288 Log.v(LOGTAG, "doTrackball pinScrollBy"
4289 + " count=" + count
4290 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04004291 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
4292 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004293 );
4294 }
4295 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
4296 xMove = 0;
4297 }
4298 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
4299 yMove = 0;
4300 }
4301 if (xMove != 0 || yMove != 0) {
4302 pinScrollBy(xMove, yMove, true, 0);
4303 }
4304 mUserScroll = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04004305 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004306 }
4307
4308 public void flingScroll(int vx, int vy) {
4309 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
4310 int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
Cary Clarkd6982c92009-05-29 11:02:22 -04004311
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004312 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, maxX, 0, maxY);
4313 invalidate();
4314 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004315
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004316 private void doFling() {
4317 if (mVelocityTracker == null) {
4318 return;
4319 }
4320 int maxX = Math.max(computeHorizontalScrollRange() - getViewWidth(), 0);
4321 int maxY = Math.max(computeVerticalScrollRange() - getViewHeight(), 0);
4322
Romain Guy4296fc42009-07-06 11:48:52 -07004323 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004324 int vx = (int) mVelocityTracker.getXVelocity();
4325 int vy = (int) mVelocityTracker.getYVelocity();
4326
4327 if (mSnapScrollMode != SNAP_NONE) {
4328 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_X_LOCK) {
4329 vy = 0;
4330 } else {
4331 vx = 0;
4332 }
4333 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004334
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004335 if (true /* EMG release: make our fling more like Maps' */) {
4336 // maps cuts their velocity in half
4337 vx = vx * 3 / 4;
4338 vy = vy * 3 / 4;
4339 }
4340
4341 mScroller.fling(mScrollX, mScrollY, -vx, -vy, 0, maxX, 0, maxY);
4342 // TODO: duration is calculated based on velocity, if the range is
4343 // small, the animation will stop before duration is up. We may
4344 // want to calculate how long the animation is going to run to precisely
4345 // resume the webcore update.
4346 final int time = mScroller.getDuration();
4347 mPrivateHandler.sendEmptyMessageDelayed(RESUME_WEBCORE_UPDATE, time);
4348 invalidate();
4349 }
4350
4351 private boolean zoomWithPreview(float scale) {
4352 float oldScale = mActualScale;
4353
Grace Kloba25737912009-06-19 12:42:47 -07004354 // snap to DEFAULT_SCALE if it is close
Grace Kloba0d8b77c2009-06-25 11:20:51 -07004355 if (scale > (mDefaultScale - 0.05) && scale < (mDefaultScale + 0.05)) {
4356 scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004357 }
4358
4359 setNewZoomScale(scale, false);
4360
4361 if (oldScale != mActualScale) {
4362 // use mZoomPickerScale to see zoom preview first
4363 mZoomStart = SystemClock.uptimeMillis();
4364 mInvInitialZoomScale = 1.0f / oldScale;
4365 mInvFinalZoomScale = 1.0f / mActualScale;
4366 mZoomScale = mActualScale;
4367 invalidate();
4368 return true;
4369 } else {
4370 return false;
4371 }
4372 }
4373
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004374 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004375 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
4376 * in charge of installing this view to the view hierarchy. This view will
4377 * become visible when the user starts scrolling via touch and fade away if
4378 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004379 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07004380 * API version 3 introduces a built-in zoom mechanism that is shown
4381 * automatically by the MapView. This is the preferred approach for
4382 * showing the zoom UI.
4383 *
4384 * @deprecated The built-in zoom mechanism is preferred, see
4385 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004386 */
The Android Open Source Project10592532009-03-18 17:39:46 -07004387 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004388 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07004389 if (!getSettings().supportZoom()) {
4390 Log.w(LOGTAG, "This WebView doesn't support zoom.");
4391 return null;
4392 }
4393 if (mZoomControls == null) {
4394 mZoomControls = createZoomControls();
Cary Clarkd6982c92009-05-29 11:02:22 -04004395
The Android Open Source Project10592532009-03-18 17:39:46 -07004396 /*
4397 * need to be set to VISIBLE first so that getMeasuredHeight() in
4398 * {@link #onSizeChanged()} can return the measured value for proper
4399 * layout.
4400 */
4401 mZoomControls.setVisibility(View.VISIBLE);
4402 mZoomControlRunnable = new Runnable() {
4403 public void run() {
Cary Clarkd6982c92009-05-29 11:02:22 -04004404
The Android Open Source Project10592532009-03-18 17:39:46 -07004405 /* Don't dismiss the controls if the user has
4406 * focus on them. Wait and check again later.
4407 */
4408 if (!mZoomControls.hasFocus()) {
4409 mZoomControls.hide();
4410 } else {
4411 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4412 mPrivateHandler.postDelayed(mZoomControlRunnable,
4413 ZOOM_CONTROLS_TIMEOUT);
4414 }
4415 }
4416 };
4417 }
4418 return mZoomControls;
4419 }
4420
4421 private ExtendedZoomControls createZoomControls() {
4422 ExtendedZoomControls zoomControls = new ExtendedZoomControls(mContext
4423 , null);
4424 zoomControls.setOnZoomInClickListener(new OnClickListener() {
4425 public void onClick(View v) {
4426 // reset time out
4427 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4428 mPrivateHandler.postDelayed(mZoomControlRunnable,
4429 ZOOM_CONTROLS_TIMEOUT);
4430 zoomIn();
4431 }
4432 });
4433 zoomControls.setOnZoomOutClickListener(new OnClickListener() {
4434 public void onClick(View v) {
4435 // reset time out
4436 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4437 mPrivateHandler.postDelayed(mZoomControlRunnable,
4438 ZOOM_CONTROLS_TIMEOUT);
4439 zoomOut();
4440 }
4441 });
4442 zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() {
4443 public void onClick(View v) {
4444 mPrivateHandler.removeCallbacks(mZoomControlRunnable);
4445 mPrivateHandler.postDelayed(mZoomControlRunnable,
4446 ZOOM_CONTROLS_TIMEOUT);
4447 zoomScrollOut();
4448 }
4449 });
4450 return zoomControls;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004451 }
4452
4453 /**
4454 * Gets the {@link ZoomButtonsController} which can be used to add
4455 * additional buttons to the zoom controls window.
Cary Clarkd6982c92009-05-29 11:02:22 -04004456 *
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004457 * @return The instance of {@link ZoomButtonsController} used by this class,
4458 * or null if it is unavailable.
The Android Open Source Project10592532009-03-18 17:39:46 -07004459 * @hide
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004460 */
4461 public ZoomButtonsController getZoomButtonsController() {
4462 return mZoomButtonsController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004463 }
4464
4465 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004466 * Perform zoom in in the webview
4467 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
4468 */
4469 public boolean zoomIn() {
4470 // TODO: alternatively we can disallow this during draw history mode
4471 switchOutDrawHistory();
4472 return zoomWithPreview(mActualScale * 1.25f);
4473 }
4474
4475 /**
4476 * Perform zoom out in the webview
4477 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
4478 */
4479 public boolean zoomOut() {
4480 // TODO: alternatively we can disallow this during draw history mode
4481 switchOutDrawHistory();
4482 return zoomWithPreview(mActualScale * 0.8f);
4483 }
4484
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004485 private void updateSelection() {
4486 if (mNativeClass == 0) {
4487 return;
4488 }
4489 // mLastTouchX and mLastTouchY are the point in the current viewport
4490 int contentX = viewToContent((int) mLastTouchX + mScrollX);
4491 int contentY = viewToContent((int) mLastTouchY + mScrollY);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07004492 Rect rect = new Rect(contentX - mNavSlop, contentY - mNavSlop,
4493 contentX + mNavSlop, contentY + mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004494 nativeSelectBestAt(rect);
4495 }
4496
Leon Scroggins0f5ad842009-07-17 15:08:34 -04004497 /**
4498 * Set our starting point and time for a drag from the WebTextView.
4499 */
4500 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
4501 if (!inEditingMode()) {
4502 return;
4503 }
4504 mLastTouchX = x + (float) (mWebTextView.getLeft() - mScrollX);
4505 mLastTouchY = y + (float) (mWebTextView.getTop() - mScrollY);
4506 mLastTouchTime = eventTime;
4507 if (!mScroller.isFinished()) {
4508 mScroller.abortAnimation();
4509 mPrivateHandler.removeMessages(RESUME_WEBCORE_UPDATE);
4510 }
4511 mSnapScrollMode = SNAP_NONE;
4512 mVelocityTracker = VelocityTracker.obtain();
4513 mTouchMode = TOUCH_DRAG_START_MODE;
4514 }
4515
4516 /**
4517 * Given a motion event from the WebTextView, set its location to our
4518 * coordinates, and handle the event.
4519 */
4520 /*package*/ boolean textFieldDrag(MotionEvent event) {
4521 if (!inEditingMode()) {
4522 return false;
4523 }
4524 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
4525 (float) (mWebTextView.getTop() - mScrollY));
4526 return onTouchEvent(event);
4527 }
4528
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004529 /*package*/ void shortPressOnTextField() {
4530 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004531 View v = mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004532 int x = viewToContent((v.getLeft() + v.getRight()) >> 1);
4533 int y = viewToContent((v.getTop() + v.getBottom()) >> 1);
Cary Clark8f9ff7e2009-05-14 10:09:26 -04004534 nativeMotionUp(x, y, mNavSlop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004535 }
4536 }
4537
4538 private void doShortPress() {
4539 if (mNativeClass == 0) {
4540 return;
4541 }
4542 switchOutDrawHistory();
4543 // mLastTouchX and mLastTouchY are the point in the current viewport
4544 int contentX = viewToContent((int) mLastTouchX + mScrollX);
4545 int contentY = viewToContent((int) mLastTouchY + mScrollY);
Cary Clark8f9ff7e2009-05-14 10:09:26 -04004546 if (nativeMotionUp(contentX, contentY, mNavSlop)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004547 if (mLogEvent) {
4548 Checkin.updateStats(mContext.getContentResolver(),
4549 Checkin.Stats.Tag.BROWSER_SNAP_CENTER, 1, 0.0);
4550 }
4551 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004552 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004553 playSoundEffect(SoundEffectConstants.CLICK);
4554 }
4555 }
4556
4557 // Called by JNI to handle a touch on a node representing an email address,
4558 // address, or phone number
4559 private void overrideLoading(String url) {
4560 mCallbackProxy.uiOverrideUrlLoading(url);
4561 }
4562
Derek Sollenberger945f3b42009-07-02 10:05:21 -04004563 // called by JNI
4564 private void sendPluginState(int state) {
4565 WebViewCore.PluginStateData psd = new WebViewCore.PluginStateData();
4566 psd.mFrame = nativeCursorFramePointer();
4567 psd.mNode = nativeCursorNodePointer();
4568 psd.mState = state;
4569 mWebViewCore.sendMessage(EventHub.PLUGIN_STATE, psd);
4570 }
4571
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004572 @Override
4573 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
4574 boolean result = false;
4575 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004576 result = mWebTextView.requestFocus(direction,
4577 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004578 } else {
4579 result = super.requestFocus(direction, previouslyFocusedRect);
4580 if (mWebViewCore.getSettings().getNeedInitialFocus()) {
4581 // For cases such as GMail, where we gain focus from a direction,
4582 // we want to move to the first available link.
4583 // FIXME: If there are no visible links, we may not want to
4584 int fakeKeyDirection = 0;
4585 switch(direction) {
4586 case View.FOCUS_UP:
4587 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
4588 break;
4589 case View.FOCUS_DOWN:
4590 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
4591 break;
4592 case View.FOCUS_LEFT:
4593 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
4594 break;
4595 case View.FOCUS_RIGHT:
4596 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
4597 break;
4598 default:
4599 return result;
4600 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004601 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Cary Clark215b72c2009-06-26 14:38:43 -04004602 navHandledKey(fakeKeyDirection, 1, true, 0, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004603 }
4604 }
4605 }
4606 return result;
4607 }
4608
4609 @Override
4610 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
4611 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
4612
4613 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
4614 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
4615 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
4616 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
4617
4618 int measuredHeight = heightSize;
4619 int measuredWidth = widthSize;
4620
4621 // Grab the content size from WebViewCore.
4622 int contentHeight = mContentHeight;
4623 int contentWidth = mContentWidth;
4624
4625// Log.d(LOGTAG, "------- measure " + heightMode);
4626
4627 if (heightMode != MeasureSpec.EXACTLY) {
4628 mHeightCanMeasure = true;
4629 measuredHeight = contentHeight;
4630 if (heightMode == MeasureSpec.AT_MOST) {
4631 // If we are larger than the AT_MOST height, then our height can
4632 // no longer be measured and we should scroll internally.
4633 if (measuredHeight > heightSize) {
4634 measuredHeight = heightSize;
4635 mHeightCanMeasure = false;
4636 }
4637 }
4638 } else {
4639 mHeightCanMeasure = false;
4640 }
4641 if (mNativeClass != 0) {
4642 nativeSetHeightCanMeasure(mHeightCanMeasure);
4643 }
4644 // For the width, always use the given size unless unspecified.
4645 if (widthMode == MeasureSpec.UNSPECIFIED) {
4646 mWidthCanMeasure = true;
4647 measuredWidth = contentWidth;
4648 } else {
4649 mWidthCanMeasure = false;
4650 }
4651
4652 synchronized (this) {
4653 setMeasuredDimension(measuredWidth, measuredHeight);
4654 }
4655 }
4656
4657 @Override
4658 public boolean requestChildRectangleOnScreen(View child,
4659 Rect rect,
4660 boolean immediate) {
4661 rect.offset(child.getLeft() - child.getScrollX(),
4662 child.getTop() - child.getScrollY());
4663
4664 int height = getHeight() - getHorizontalScrollbarHeight();
4665 int screenTop = mScrollY;
4666 int screenBottom = screenTop + height;
4667
4668 int scrollYDelta = 0;
4669
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004670 if (rect.bottom > screenBottom) {
4671 int oneThirdOfScreenHeight = height / 3;
4672 if (rect.height() > 2 * oneThirdOfScreenHeight) {
4673 // If the rectangle is too tall to fit in the bottom two thirds
4674 // of the screen, place it at the top.
4675 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004676 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004677 // If the rectangle will still fit on screen, we want its
4678 // top to be in the top third of the screen.
4679 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004680 }
4681 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07004682 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004683 }
4684
4685 int width = getWidth() - getVerticalScrollbarWidth();
4686 int screenLeft = mScrollX;
4687 int screenRight = screenLeft + width;
4688
4689 int scrollXDelta = 0;
4690
4691 if (rect.right > screenRight && rect.left > screenLeft) {
4692 if (rect.width() > width) {
4693 scrollXDelta += (rect.left - screenLeft);
4694 } else {
4695 scrollXDelta += (rect.right - screenRight);
4696 }
4697 } else if (rect.left < screenLeft) {
4698 scrollXDelta -= (screenLeft - rect.left);
4699 }
4700
4701 if ((scrollYDelta | scrollXDelta) != 0) {
4702 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
4703 }
4704
4705 return false;
4706 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004707
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004708 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
4709 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04004710 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
4711 arg.mReplace = replace;
4712 arg.mNewStart = newStart;
4713 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004714 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04004715 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004716 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
4717 }
4718
4719 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkf958fcc2009-06-12 17:33:37 -04004720 if (nativeCursorWantsKeyEvents() && !nativeCursorMatchesFocus()) {
4721 mWebViewCore.sendMessage(EventHub.CLICK);
4722 }
Cary Clarkded054c2009-06-15 10:26:08 -04004723 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
4724 arg.mEvent = event;
4725 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004726 // Increase our text generation number, and pass it to webcore thread
4727 mTextGeneration++;
4728 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
4729 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04004730 // To make sure the host application, like Browser, has the up to date
4731 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004732 // document state.
4733 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
4734 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04004735 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004736 }
4737
4738 /* package */ WebViewCore getWebViewCore() {
4739 return mWebViewCore;
4740 }
4741
4742 //-------------------------------------------------------------------------
4743 // Methods can be called from a separate thread, like WebViewCore
4744 // If it needs to call the View system, it has to send message.
4745 //-------------------------------------------------------------------------
4746
4747 /**
4748 * General handler to receive message coming from webkit thread
4749 */
4750 class PrivateHandler extends Handler {
4751 @Override
4752 public void handleMessage(Message msg) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004753 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004754 Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
4755 > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004756 : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
4757 }
4758 switch (msg.what) {
4759 case REMEMBER_PASSWORD: {
4760 mDatabase.setUsernamePassword(
4761 msg.getData().getString("host"),
4762 msg.getData().getString("username"),
4763 msg.getData().getString("password"));
4764 ((Message) msg.obj).sendToTarget();
4765 break;
4766 }
4767 case NEVER_REMEMBER_PASSWORD: {
4768 mDatabase.setUsernamePassword(
4769 msg.getData().getString("host"), null, null);
4770 ((Message) msg.obj).sendToTarget();
4771 break;
4772 }
4773 case SWITCH_TO_SHORTPRESS: {
4774 if (mTouchMode == TOUCH_INIT_MODE) {
4775 mTouchMode = TOUCH_SHORTPRESS_START_MODE;
4776 updateSelection();
4777 }
4778 break;
4779 }
4780 case SWITCH_TO_LONGPRESS: {
Grace Kloba6c451b72009-06-25 12:25:30 -07004781 if (!mPreventDrag) {
4782 mTouchMode = TOUCH_DONE_MODE;
4783 performLongClick();
Grace Kloba959046c2009-06-25 14:28:04 -07004784 rebuildWebTextView();
Grace Kloba6c451b72009-06-25 12:25:30 -07004785 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004786 break;
4787 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004788 case SWITCH_TO_CLICK:
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004789 // The user clicked with the trackball, and did not click a
4790 // second time, so perform the action of a trackball single
4791 // click
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004792 mTouchMode = TOUCH_DONE_MODE;
Cary Clarkd6982c92009-05-29 11:02:22 -04004793 Rect visibleRect = sendOurVisibleRect();
4794 // Note that sendOurVisibleRect calls viewToContent, so the
4795 // coordinates should be in content coordinates.
4796 if (!nativeCursorIntersects(visibleRect)) {
4797 break;
4798 }
4799 nativeSetFollowedLink(true);
Cary Clark215b72c2009-06-26 14:38:43 -04004800 nativeUpdatePluginReceivesEvents();
Leon Scrogginsef6da8f2009-06-26 14:00:40 -04004801 WebViewCore.CursorData data = cursorData();
4802 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
Cary Clarkd6982c92009-05-29 11:02:22 -04004803 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004804 boolean isTextInput = nativeCursorIsTextInput();
4805 if (isTextInput || !mCallbackProxy.uiOverrideUrlLoading(
4806 nativeCursorText())) {
Leon Scrogginsef6da8f2009-06-26 14:00:40 -04004807 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
4808 nativeCursorNodePointer());
Cary Clarkd6982c92009-05-29 11:02:22 -04004809 }
Leon Scroggins1bd0d6a2009-06-16 15:04:28 -04004810 if (isTextInput) {
4811 rebuildWebTextView();
4812 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004813 break;
4814 case SCROLL_BY_MSG_ID:
4815 setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
4816 break;
4817 case SYNC_SCROLL_TO_MSG_ID:
4818 if (mUserScroll) {
Cary Clarkd6982c92009-05-29 11:02:22 -04004819 // if user has scrolled explicitly, don't sync the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004820 // scroll position any more
4821 mUserScroll = false;
4822 break;
4823 }
4824 // fall through
4825 case SCROLL_TO_MSG_ID:
4826 if (setContentScrollTo(msg.arg1, msg.arg2)) {
4827 // if we can't scroll to the exact position due to pin,
Cary Clarkd6982c92009-05-29 11:02:22 -04004828 // send a message to WebCore to re-scroll when we get a
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004829 // new picture
4830 mUserScroll = false;
4831 mWebViewCore.sendMessage(EventHub.SYNC_SCROLL,
4832 msg.arg1, msg.arg2);
4833 }
4834 break;
4835 case SPAWN_SCROLL_TO_MSG_ID:
4836 spawnContentScrollTo(msg.arg1, msg.arg2);
4837 break;
4838 case NEW_PICTURE_MSG_ID:
4839 // called for new content
Cary Clarkd6982c92009-05-29 11:02:22 -04004840 final WebViewCore.DrawData draw =
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004841 (WebViewCore.DrawData) msg.obj;
4842 final Point viewSize = draw.mViewPoint;
4843 if (mZoomScale > 0) {
4844 // use the same logic in sendViewSizeZoom() to make sure
4845 // the mZoomScale has matched the viewSize so that we
4846 // can clear mZoomScale
4847 if (Math.round(getViewWidth() / mZoomScale) == viewSize.x) {
4848 mZoomScale = 0;
4849 mWebViewCore.sendMessage(EventHub.SET_SNAP_ANCHOR,
4850 0, 0);
4851 }
4852 }
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004853 if (!mMinZoomScaleFixed) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004854 mMinZoomScale = (float) getViewWidth()
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004855 / Math.max(ZOOM_OUT_WIDTH, draw.mWidthHeight.x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004856 }
4857 // We update the layout (i.e. request a layout from the
4858 // view system) if the last view size that we sent to
4859 // WebCore matches the view size of the picture we just
4860 // received in the fixed dimension.
4861 final boolean updateLayout = viewSize.x == mLastWidthSent
4862 && viewSize.y == mLastHeightSent;
Cary Clarkd6982c92009-05-29 11:02:22 -04004863 recordNewContentSize(draw.mWidthHeight.x,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004864 draw.mWidthHeight.y, updateLayout);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004865 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004866 Rect b = draw.mInvalRegion.getBounds();
4867 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
4868 b.left+","+b.top+","+b.right+","+b.bottom+"}");
4869 }
4870 invalidate(contentToView(draw.mInvalRegion.getBounds()));
4871 if (mPictureListener != null) {
4872 mPictureListener.onNewPicture(WebView.this, capturePicture());
4873 }
4874 break;
4875 case WEBCORE_INITIALIZED_MSG_ID:
4876 // nativeCreate sets mNativeClass to a non-zero value
4877 nativeCreate(msg.arg1);
4878 break;
4879 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
4880 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04004881 // and representing the same node as the pointer.
4882 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004883 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004884 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004885 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004886 int start = Selection.getSelectionStart(text);
4887 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004888 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004889 // Restore the selection, which may have been
4890 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004891 Spannable pword =
4892 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004893 Selection.setSelection(pword, start, end);
4894 // If the text entry has created more events, ignore
4895 // this one.
4896 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004897 mWebTextView.setTextAndKeepSelection(
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004898 (String) msg.obj);
4899 }
4900 }
4901 break;
4902 case DID_FIRST_LAYOUT_MSG_ID:
4903 if (mNativeClass == 0) {
4904 break;
4905 }
Cary Clarkded054c2009-06-15 10:26:08 -04004906 ScaleLimitData scaleLimit = (ScaleLimitData) msg.obj;
4907 int minScale = scaleLimit.mMinScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004908 if (minScale == 0) {
4909 mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004910 mMinZoomScaleFixed = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004911 } else {
4912 mMinZoomScale = (float) (minScale / 100.0);
The Android Open Source Projectba87e3e2009-03-13 13:04:22 -07004913 mMinZoomScaleFixed = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004914 }
Cary Clarkded054c2009-06-15 10:26:08 -04004915 int maxScale = scaleLimit.mMaxScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004916 if (maxScale == 0) {
4917 mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
4918 } else {
4919 mMaxZoomScale = (float) (maxScale / 100.0);
4920 }
4921 // If history Picture is drawn, don't update zoomWidth
4922 if (mDrawHistory) {
4923 break;
4924 }
4925 int width = getViewWidth();
4926 if (width == 0) {
4927 break;
4928 }
4929 int initialScale = msg.arg1;
4930 int viewportWidth = msg.arg2;
Grace Kloba25737912009-06-19 12:42:47 -07004931 // start a new page with DEFAULT_SCALE zoom scale.
Grace Kloba0d8b77c2009-06-25 11:20:51 -07004932 float scale = mDefaultScale;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004933 if (mInitialScale > 0) {
4934 scale = mInitialScale / 100.0f;
4935 } else {
Grace Kloba45dee412009-04-06 10:47:26 -07004936 if (initialScale < 0) break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004937 if (mWebViewCore.getSettings().getUseWideViewPort()) {
4938 // force viewSizeChanged by setting mLastWidthSent
4939 // to 0
4940 mLastWidthSent = 0;
4941 }
4942 if (initialScale == 0) {
4943 // if viewportWidth is defined and it is smaller
4944 // than the view width, zoom in to fill the view
4945 if (viewportWidth > 0 && viewportWidth < width) {
4946 scale = (float) width / viewportWidth;
4947 }
4948 } else {
4949 scale = initialScale / 100.0f;
4950 }
4951 }
4952 setNewZoomScale(scale, false);
Leon Scroggins10c7a4d2009-07-20 11:54:05 -04004953 // As we are on a new page, remove the WebTextView. This
4954 // is necessary for page loads driven by webkit, and in
4955 // particular when the user was on a password field, so
4956 // the WebTextView was visible.
4957 clearTextEntry();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004958 break;
Cary Clark215b72c2009-06-26 14:38:43 -04004959 case MOVE_OUT_OF_PLUGIN:
4960 if (nativePluginEatsNavKey()) {
4961 navHandledKey(msg.arg1, 1, false, 0, true);
4962 }
4963 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004964 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04004965 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004966 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04004967 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004968 mWebTextView.bringIntoView();
Leon Scroggins4890feb2009-07-02 10:37:10 -04004969 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004970 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004971 break;
Cary Clark243ea062009-06-25 10:49:32 -04004972 case CLEAR_TEXT_ENTRY:
4973 clearTextEntry();
4974 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004975 case INVAL_RECT_MSG_ID: {
4976 Rect r = (Rect)msg.obj;
4977 if (r == null) {
4978 invalidate();
4979 } else {
4980 // we need to scale r from content into view coords,
4981 // which viewInvalidate() does for us
4982 viewInvalidate(r.left, r.top, r.right, r.bottom);
4983 }
4984 break;
4985 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004986 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04004987 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004988 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004989 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004990 }
4991 break;
4992 case UPDATE_CLIPBOARD:
4993 String str = (String) msg.obj;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004994 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004995 Log.v(LOGTAG, "UPDATE_CLIPBOARD " + str);
4996 }
4997 try {
4998 IClipboard clip = IClipboard.Stub.asInterface(
4999 ServiceManager.getService("clipboard"));
5000 clip.setClipboardText(str);
5001 } catch (android.os.RemoteException e) {
5002 Log.e(LOGTAG, "Clipboard failed", e);
5003 }
5004 break;
5005 case RESUME_WEBCORE_UPDATE:
5006 WebViewCore.resumeUpdate(mWebViewCore);
5007 break;
5008
Leon Scrogginse3225672009-06-03 15:53:13 -04005009 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005010 // as this is shared by keydown and trackballdown, reset all
5011 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04005012 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005013 mTrackballDown = false;
Leon Scrogginse3225672009-06-03 15:53:13 -04005014 // LONG_PRESS_CENTER is sent as a delayed message. If we
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005015 // switch to windows overview, the WebView will be
5016 // temporarily removed from the view system. In that case,
5017 // do nothing.
5018 if (getParent() != null) {
5019 performLongClick();
5020 }
5021 break;
5022
5023 case WEBCORE_NEED_TOUCH_EVENTS:
5024 mForwardTouchEvents = (msg.arg1 != 0);
5025 break;
5026
5027 case PREVENT_TOUCH_ID:
5028 if (msg.arg1 == MotionEvent.ACTION_DOWN) {
5029 mPreventDrag = msg.arg2 == 1;
5030 if (mPreventDrag) {
5031 mTouchMode = TOUCH_DONE_MODE;
5032 }
5033 }
5034 break;
5035
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04005036 case REQUEST_KEYBOARD:
5037 if (msg.arg1 == 0) {
5038 hideSoftKeyboard();
5039 } else {
5040 displaySoftKeyboard(false);
5041 }
5042 break;
5043
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005044 default:
5045 super.handleMessage(msg);
5046 break;
5047 }
5048 }
5049 }
5050
5051 // Class used to use a dropdown for a <select> element
5052 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005053 // Whether the listbox allows multiple selection.
5054 private boolean mMultiple;
5055 // Passed in to a list with multiple selection to tell
5056 // which items are selected.
5057 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04005058 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005059 // where the initial selection is.
5060 private int mSelection;
5061
5062 private Container[] mContainers;
5063
5064 // Need these to provide stable ids to my ArrayAdapter,
5065 // which normally does not have stable ids. (Bug 1250098)
5066 private class Container extends Object {
5067 String mString;
5068 boolean mEnabled;
5069 int mId;
5070
5071 public String toString() {
5072 return mString;
5073 }
5074 }
5075
5076 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04005077 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005078 * and allow filtering.
5079 */
5080 private class MyArrayListAdapter extends ArrayAdapter<Container> {
5081 public MyArrayListAdapter(Context context, Container[] objects, boolean multiple) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005082 super(context,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005083 multiple ? com.android.internal.R.layout.select_dialog_multichoice :
Cary Clarkd6982c92009-05-29 11:02:22 -04005084 com.android.internal.R.layout.select_dialog_singlechoice,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005085 objects);
5086 }
5087
5088 @Override
5089 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04005090 // AdapterView's onChanged method uses this to determine whether
5091 // to restore the old state. Return false so that the old (out
5092 // of date) state does not replace the new, valid state.
5093 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005094 }
5095
5096 private Container item(int position) {
5097 if (position < 0 || position >= getCount()) {
5098 return null;
5099 }
5100 return (Container) getItem(position);
5101 }
5102
5103 @Override
5104 public long getItemId(int position) {
5105 Container item = item(position);
5106 if (item == null) {
5107 return -1;
5108 }
5109 return item.mId;
5110 }
5111
5112 @Override
5113 public boolean areAllItemsEnabled() {
5114 return false;
5115 }
5116
5117 @Override
5118 public boolean isEnabled(int position) {
5119 Container item = item(position);
5120 if (item == null) {
5121 return false;
5122 }
5123 return item.mEnabled;
5124 }
5125 }
5126
5127 private InvokeListBox(String[] array,
5128 boolean[] enabled, int[] selected) {
5129 mMultiple = true;
5130 mSelectedArray = selected;
5131
5132 int length = array.length;
5133 mContainers = new Container[length];
5134 for (int i = 0; i < length; i++) {
5135 mContainers[i] = new Container();
5136 mContainers[i].mString = array[i];
5137 mContainers[i].mEnabled = enabled[i];
5138 mContainers[i].mId = i;
5139 }
5140 }
5141
Cary Clarkd6982c92009-05-29 11:02:22 -04005142 private InvokeListBox(String[] array, boolean[] enabled, int
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005143 selection) {
5144 mSelection = selection;
5145 mMultiple = false;
5146
5147 int length = array.length;
5148 mContainers = new Container[length];
5149 for (int i = 0; i < length; i++) {
5150 mContainers[i] = new Container();
5151 mContainers[i].mString = array[i];
5152 mContainers[i].mEnabled = enabled[i];
5153 mContainers[i].mId = i;
5154 }
5155 }
5156
Leon Scroggins3667ce42009-05-13 15:58:03 -04005157 /*
5158 * Whenever the data set changes due to filtering, this class ensures
5159 * that the checked item remains checked.
5160 */
5161 private class SingleDataSetObserver extends DataSetObserver {
5162 private long mCheckedId;
5163 private ListView mListView;
5164 private Adapter mAdapter;
5165
5166 /*
5167 * Create a new observer.
5168 * @param id The ID of the item to keep checked.
5169 * @param l ListView for getting and clearing the checked states
5170 * @param a Adapter for getting the IDs
5171 */
5172 public SingleDataSetObserver(long id, ListView l, Adapter a) {
5173 mCheckedId = id;
5174 mListView = l;
5175 mAdapter = a;
5176 }
5177
5178 public void onChanged() {
5179 // The filter may have changed which item is checked. Find the
5180 // item that the ListView thinks is checked.
5181 int position = mListView.getCheckedItemPosition();
5182 long id = mAdapter.getItemId(position);
5183 if (mCheckedId != id) {
5184 // Clear the ListView's idea of the checked item, since
5185 // it is incorrect
5186 mListView.clearChoices();
5187 // Search for mCheckedId. If it is in the filtered list,
5188 // mark it as checked
5189 int count = mAdapter.getCount();
5190 for (int i = 0; i < count; i++) {
5191 if (mAdapter.getItemId(i) == mCheckedId) {
5192 mListView.setItemChecked(i, true);
5193 break;
5194 }
5195 }
5196 }
5197 }
5198
5199 public void onInvalidate() {}
5200 }
5201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005202 public void run() {
5203 final ListView listView = (ListView) LayoutInflater.from(mContext)
5204 .inflate(com.android.internal.R.layout.select_dialog, null);
Cary Clarkd6982c92009-05-29 11:02:22 -04005205 final MyArrayListAdapter adapter = new
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005206 MyArrayListAdapter(mContext, mContainers, mMultiple);
5207 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
5208 .setView(listView).setCancelable(true)
5209 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04005210
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005211 if (mMultiple) {
5212 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
5213 public void onClick(DialogInterface dialog, int which) {
5214 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04005215 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005216 adapter.getCount(), 0,
5217 listView.getCheckedItemPositions());
5218 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07005219 b.setNegativeButton(android.R.string.cancel,
5220 new DialogInterface.OnClickListener() {
5221 public void onClick(DialogInterface dialog, int which) {
5222 mWebViewCore.sendMessage(
5223 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5224 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005225 }
5226 final AlertDialog dialog = b.create();
5227 listView.setAdapter(adapter);
5228 listView.setFocusableInTouchMode(true);
5229 // There is a bug (1250103) where the checks in a ListView with
5230 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04005231 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005232 // filtered. Do not allow filtering on multiple lists until
5233 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04005234
Leon Scroggins3667ce42009-05-13 15:58:03 -04005235 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005236 if (mMultiple) {
5237 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
5238 int length = mSelectedArray.length;
5239 for (int i = 0; i < length; i++) {
5240 listView.setItemChecked(mSelectedArray[i], true);
5241 }
5242 } else {
5243 listView.setOnItemClickListener(new OnItemClickListener() {
5244 public void onItemClick(AdapterView parent, View v,
5245 int position, long id) {
5246 mWebViewCore.sendMessage(
5247 EventHub.SINGLE_LISTBOX_CHOICE, (int)id, 0);
5248 dialog.dismiss();
5249 }
5250 });
5251 if (mSelection != -1) {
5252 listView.setSelection(mSelection);
5253 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
5254 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04005255 DataSetObserver observer = new SingleDataSetObserver(
5256 adapter.getItemId(mSelection), listView, adapter);
5257 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005258 }
5259 }
5260 dialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
5261 public void onCancel(DialogInterface dialog) {
5262 mWebViewCore.sendMessage(
5263 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
5264 }
5265 });
5266 dialog.show();
5267 }
5268 }
5269
5270 /*
5271 * Request a dropdown menu for a listbox with multiple selection.
5272 *
5273 * @param array Labels for the listbox.
5274 * @param enabledArray Which positions are enabled.
5275 * @param selectedArray Which positions are initally selected.
5276 */
5277 void requestListBox(String[] array, boolean[]enabledArray, int[]
5278 selectedArray) {
5279 mPrivateHandler.post(
5280 new InvokeListBox(array, enabledArray, selectedArray));
5281 }
5282
5283 /*
5284 * Request a dropdown menu for a listbox with single selection or a single
5285 * <select> element.
5286 *
5287 * @param array Labels for the listbox.
5288 * @param enabledArray Which positions are enabled.
5289 * @param selection Which position is initally selected.
5290 */
5291 void requestListBox(String[] array, boolean[]enabledArray, int selection) {
5292 mPrivateHandler.post(
5293 new InvokeListBox(array, enabledArray, selection));
5294 }
5295
5296 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005297 private void sendMoveMouse(int frame, int node, int x, int y) {
5298 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
5299 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005300 }
5301
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005302 /*
5303 * Send a mouse move event to the webcore thread.
5304 *
5305 * @param removeFocus Pass true if the "mouse" cursor is now over a node
5306 * which wants key events, but it is not the focus. This
5307 * will make the visual appear as though nothing is in
5308 * focus. Remove the WebTextView, if present, and stop
5309 * drawing the blinking caret.
5310 * called by JNI
5311 */
5312 private void sendMoveMouseIfLatest(boolean removeFocus) {
5313 if (removeFocus) {
5314 clearTextEntry();
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04005315 setFocusControllerInactive();
Cary Clark19436562009-06-04 16:25:07 -04005316 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04005317 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
5318 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005319 }
5320
5321 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04005322 private void sendMotionUp(int touchGeneration,
5323 int frame, int node, int x, int y, int size) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005324 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
5325 touchUpData.mMoveGeneration = touchGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005326 touchUpData.mSize = size;
Cary Clarkd6982c92009-05-29 11:02:22 -04005327 touchUpData.mFrame = frame;
5328 touchUpData.mNode = node;
5329 touchUpData.mX = x;
5330 touchUpData.mY = y;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005331 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
5332 }
5333
5334
5335 private int getScaledMaxXScroll() {
5336 int width;
5337 if (mHeightCanMeasure == false) {
5338 width = getViewWidth() / 4;
5339 } else {
5340 Rect visRect = new Rect();
5341 calcOurVisibleRect(visRect);
5342 width = visRect.width() / 2;
5343 }
5344 // FIXME the divisor should be retrieved from somewhere
5345 return viewToContent(width);
5346 }
5347
5348 private int getScaledMaxYScroll() {
5349 int height;
5350 if (mHeightCanMeasure == false) {
5351 height = getViewHeight() / 4;
5352 } else {
5353 Rect visRect = new Rect();
5354 calcOurVisibleRect(visRect);
5355 height = visRect.height() / 2;
5356 }
5357 // FIXME the divisor should be retrieved from somewhere
5358 // the closest thing today is hard-coded into ScrollView.java
5359 // (from ScrollView.java, line 363) int maxJump = height/2;
5360 return viewToContent(height);
5361 }
5362
5363 /**
5364 * Called by JNI to invalidate view
5365 */
5366 private void viewInvalidate() {
5367 invalidate();
5368 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005370 // return true if the key was handled
Cary Clark215b72c2009-06-26 14:38:43 -04005371 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
5372 long time, boolean ignorePlugin) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005373 if (mNativeClass == 0) {
5374 return false;
5375 }
Cary Clark215b72c2009-06-26 14:38:43 -04005376 if (ignorePlugin == false && nativePluginEatsNavKey()) {
5377 KeyEvent event = new KeyEvent(time, time, KeyEvent.ACTION_DOWN
5378 , keyCode, count, (mShiftIsPressed ? KeyEvent.META_SHIFT_ON : 0)
5379 | (false ? KeyEvent.META_ALT_ON : 0) // FIXME
5380 | (false ? KeyEvent.META_SYM_ON : 0) // FIXME
5381 , 0, 0, 0);
5382 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
5383 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
5384 return true;
5385 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005386 mLastCursorTime = time;
5387 mLastCursorBounds = nativeGetCursorRingBounds();
5388 boolean keyHandled
5389 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005390 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005391 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
5392 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005393 + " handled=" + keyHandled);
5394 }
5395 if (keyHandled == false || mHeightCanMeasure == false) {
5396 return keyHandled;
5397 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005398 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
5399 if (contentCursorRingBounds.isEmpty()) return keyHandled;
5400 Rect viewCursorRingBounds = contentToView(contentCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005401 Rect visRect = new Rect();
5402 calcOurVisibleRect(visRect);
5403 Rect outset = new Rect(visRect);
5404 int maxXScroll = visRect.width() / 2;
5405 int maxYScroll = visRect.height() / 2;
5406 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005407 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005408 return keyHandled;
5409 }
5410 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005411 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
5412 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005413 if (maxH > 0) {
5414 pinScrollBy(maxH, 0, true, 0);
5415 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005416 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
5417 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005418 if (maxH < 0) {
5419 pinScrollBy(maxH, 0, true, 0);
5420 }
5421 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005422 if (mLastCursorBounds.isEmpty()) return keyHandled;
5423 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
5424 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005425 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005426 if (DebugFlags.WEB_VIEW) {
5427 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
5428 + contentCursorRingBounds);
5429 }
5430 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005431 mUserScroll = true;
5432 return keyHandled;
5433 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005434
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005435 /**
5436 * Set the background color. It's white by default. Pass
5437 * zero to make the view transparent.
5438 * @param color the ARGB color described by Color.java
5439 */
5440 public void setBackgroundColor(int color) {
5441 mBackgroundColor = color;
5442 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
5443 }
5444
5445 public void debugDump() {
5446 nativeDebugDump();
5447 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
5448 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005449
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005450 /**
5451 * Update our cache with updatedText.
5452 * @param updatedText The new text to put in our cache.
5453 */
5454 /* package */ void updateCachedTextfield(String updatedText) {
5455 // Also place our generation number so that when we look at the cache
5456 // we recognize that it is up to date.
5457 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
5458 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005459
Leon Scroggins4890feb2009-07-02 10:37:10 -04005460 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005461 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04005462 private native int nativeCursorFramePointer();
5463 private native Rect nativeCursorNodeBounds();
5464 /* package */ native int nativeCursorNodePointer();
5465 /* package */ native boolean nativeCursorMatchesFocus();
5466 private native boolean nativeCursorIntersects(Rect visibleRect);
5467 private native boolean nativeCursorIsAnchor();
Cary Clark215b72c2009-06-26 14:38:43 -04005468 private native boolean nativeCursorIsPlugin();
Cary Clarkd6982c92009-05-29 11:02:22 -04005469 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04005470 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04005471 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005472 /**
5473 * Returns true if the native cursor node says it wants to handle key events
5474 * (ala plugins). This can only be called if mNativeClass is non-zero!
5475 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04005476 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005477 private native void nativeDebugDump();
5478 private native void nativeDestroy();
Cary Clarkd6982c92009-05-29 11:02:22 -04005479 private native void nativeDrawCursorRing(Canvas content);
5480 private native void nativeDrawMatches(Canvas canvas);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005481 private native void nativeDrawSelection(Canvas content
5482 , int x, int y, boolean extendSelection);
5483 private native void nativeDrawSelectionRegion(Canvas content);
Cary Clarkd6982c92009-05-29 11:02:22 -04005484 private native void nativeDumpDisplayTree(String urlOrNull);
5485 private native int nativeFindAll(String findLower, String findUpper);
5486 private native void nativeFindNext(boolean forward);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005487 private native boolean nativeFocusCandidateIsPassword();
5488 private native boolean nativeFocusCandidateIsRtlText();
5489 private native boolean nativeFocusCandidateIsTextField();
5490 private native boolean nativeFocusCandidateIsTextInput();
5491 private native int nativeFocusCandidateMaxLength();
Leon Scroggins0ca70882009-06-26 17:45:29 -04005492 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005493 private native Rect nativeFocusCandidateNodeBounds();
5494 /* package */ native int nativeFocusCandidatePointer();
5495 private native String nativeFocusCandidateText();
5496 private native int nativeFocusCandidateTextSize();
Leon Scroggins4890feb2009-07-02 10:37:10 -04005497 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04005498 private native Rect nativeGetCursorRingBounds();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005499 private native Region nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04005500 private native boolean nativeHasCursorNode();
5501 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04005502 private native void nativeHideCursor();
Cary Clarkd6982c92009-05-29 11:02:22 -04005503 private native String nativeImageURI(int x, int y);
5504 private native void nativeInstrumentReport();
Cary Clarkd6982c92009-05-29 11:02:22 -04005505 // return true if the page has been scrolled
5506 private native boolean nativeMotionUp(int x, int y, int slop);
5507 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04005508 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04005509 boolean noScroll);
5510 private native int nativeMoveGeneration();
5511 private native void nativeMoveSelection(int x, int y,
5512 boolean extendSelection);
Cary Clark215b72c2009-06-26 14:38:43 -04005513 private native boolean nativePluginEatsNavKey();
Cary Clarkd6982c92009-05-29 11:02:22 -04005514 // Like many other of our native methods, you must make sure that
5515 // mNativeClass is not null before calling this method.
5516 private native void nativeRecordButtons(boolean focused,
5517 boolean pressed, boolean invalidate);
5518 private native void nativeSelectBestAt(Rect rect);
5519 private native void nativeSetFindIsDown();
5520 private native void nativeSetFollowedLink(boolean followed);
5521 private native void nativeSetHeightCanMeasure(boolean measure);
5522 private native int nativeTextGeneration();
5523 // Never call this version except by updateCachedTextfield(String) -
5524 // we always want to pass in our generation number.
5525 private native void nativeUpdateCachedTextfield(String updatedText,
5526 int generation);
Cary Clark215b72c2009-06-26 14:38:43 -04005527 private native void nativeUpdatePluginReceivesEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005528}