blob: cf83456f9dee9ed49f809bc1035d3c386247543a [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.webkit;
18
Raphael30df2372010-03-06 10:09:54 -080019import android.annotation.Widget;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080020import android.app.AlertDialog;
Derek Sollenberger41d5e932010-08-23 14:51:41 -040021import android.content.BroadcastReceiver;
Dianne Hackborn90d5e8c2010-08-05 13:47:55 -070022import android.content.ClipboardManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
24import android.content.DialogInterface;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.DialogInterface.OnCancelListener;
Adam Powell637d3372010-08-25 14:37:03 -070026import android.content.Intent;
27import android.content.IntentFilter;
Derek Sollenberger41d5e932010-08-23 14:51:41 -040028import android.content.pm.PackageInfo;
29import android.content.pm.PackageManager;
Cary Clarkc5cd5e92010-11-22 15:20:02 -050030import android.content.res.Configuration;
Leon Scroggins3667ce42009-05-13 15:58:03 -040031import android.database.DataSetObserver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.graphics.Bitmap;
Adam Powell637d3372010-08-25 14:37:03 -070033import android.graphics.BitmapFactory;
34import android.graphics.BitmapShader;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.graphics.Canvas;
36import android.graphics.Color;
Grace Kloba178db412010-05-18 22:22:23 -070037import android.graphics.CornerPathEffect;
Grace Kloba8abd50b2010-07-08 15:02:14 -070038import android.graphics.DrawFilter;
Grace Kloba178db412010-05-18 22:22:23 -070039import android.graphics.Paint;
Grace Kloba8abd50b2010-07-08 15:02:14 -070040import android.graphics.PaintFlagsDrawFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.graphics.Picture;
42import android.graphics.Point;
43import android.graphics.Rect;
Nicolas Roard46318cf2010-05-10 15:15:51 -070044import android.graphics.RectF;
Grace Kloba178db412010-05-18 22:22:23 -070045import android.graphics.Region;
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -080046import android.graphics.SurfaceTexture;
Adam Powell637d3372010-08-25 14:37:03 -070047import android.graphics.Shader;
Mike Reede8853fc2009-09-04 14:01:48 -040048import android.graphics.drawable.Drawable;
Kristian Monsen41e7e6f2010-12-21 12:51:11 +000049import android.net.Proxy;
50import android.net.ProxyProperties;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.net.Uri;
Adam Powell9d32d242010-03-29 16:02:07 -070052import android.net.http.SslCertificate;
Derek Sollenberger41d5e932010-08-23 14:51:41 -040053import android.os.AsyncTask;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.os.Bundle;
55import android.os.Handler;
56import android.os.Message;
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -070057import android.provider.Settings;
Svetoslav Ganovda355512010-05-12 22:04:44 -070058import android.speech.tts.TextToSpeech;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.text.Selection;
60import android.text.Spannable;
61import android.util.AttributeSet;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.util.EventLog;
63import android.util.Log;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064import android.view.Gravity;
Svetoslav Ganov9504f572011-01-14 11:38:17 -080065import android.view.HardwareCanvas;
Jeff Brown33bbfd22011-02-24 20:55:35 -080066import android.view.InputDevice;
Jeff Brown6b53e8d2010-11-10 16:03:06 -080067import android.view.KeyCharacterMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.view.KeyEvent;
69import android.view.LayoutInflater;
70import android.view.MotionEvent;
Grace Kloba3a0def22010-01-23 21:11:54 -080071import android.view.ScaleGestureDetector;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072import android.view.SoundEffectConstants;
73import android.view.VelocityTracker;
74import android.view.View;
75import android.view.ViewConfiguration;
76import android.view.ViewGroup;
Leon Scroggins7f7bd522010-11-05 11:23:31 -040077import android.view.ViewParent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078import android.view.ViewTreeObserver;
Svetoslav Ganovda355512010-05-12 22:04:44 -070079import android.view.accessibility.AccessibilityManager;
Derek Sollenberger7cabb032010-01-21 10:37:38 -050080import android.view.inputmethod.EditorInfo;
81import android.view.inputmethod.InputConnection;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082import android.view.inputmethod.InputMethodManager;
Leon Scrogginsd3465f62009-06-02 10:57:54 -040083import android.webkit.WebTextView.AutoCompleteAdapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084import android.webkit.WebViewCore.EventHub;
Grace Klobac2242f22010-03-05 14:00:26 -080085import android.webkit.WebViewCore.TouchEventData;
Grace Kloba178db412010-05-18 22:22:23 -070086import android.webkit.WebViewCore.TouchHighlightData;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087import android.widget.AbsoluteLayout;
Leon Scroggins3667ce42009-05-13 15:58:03 -040088import android.widget.Adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089import android.widget.AdapterView;
Michael Kolb73980a92010-08-05 16:32:51 -070090import android.widget.AdapterView.OnItemClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091import android.widget.ArrayAdapter;
Leon Scrogginsa8da1732009-10-19 19:04:30 -040092import android.widget.CheckedTextView;
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -040093import android.widget.LinearLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094import android.widget.ListView;
Adam Powell637d3372010-08-25 14:37:03 -070095import android.widget.OverScroller;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096import android.widget.Toast;
Michael Kolb73980a92010-08-05 16:32:51 -070097
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080098import java.io.File;
99import java.io.FileInputStream;
100import java.io.FileNotFoundException;
101import java.io.FileOutputStream;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102import java.net.URLDecoder;
103import java.util.ArrayList;
Andrei Popescuf5dba882010-01-12 22:42:41 +0000104import java.util.HashMap;
Derek Sollenberger41d5e932010-08-23 14:51:41 -0400105import java.util.HashSet;
Adam Powell9d32d242010-03-29 16:02:07 -0700106import java.util.List;
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100107import java.util.Map;
Andrei Popescua6d747d2010-02-11 13:19:21 +0000108import java.util.Set;
Leon Scroggins115626a2011-02-17 12:00:48 -0500109import java.util.Vector;
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -0700110import java.util.regex.Matcher;
111import java.util.regex.Pattern;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
Adam Powell637d3372010-08-25 14:37:03 -0700113import junit.framework.Assert;
114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800115/**
Cary Clarkd6982c92009-05-29 11:02:22 -0400116 * <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 -0800117 * can roll your own web browser or simply display some online content within your Activity.
118 * It uses the WebKit rendering engine to display
119 * web pages and includes methods to navigate forward and backward
120 * through a history, zoom in and out, perform text searches and more.</p>
The Android Open Source Project10592532009-03-18 17:39:46 -0700121 * <p>To enable the built-in zoom, set
122 * {@link #getSettings() WebSettings}.{@link WebSettings#setBuiltInZoomControls(boolean)}
123 * (introduced in API version 3).
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800124 * <p>Note that, in order for your Activity to access the Internet and load web pages
Scott Main8b3cea02010-05-14 14:12:43 -0700125 * in a WebView, you must add the {@code INTERNET} permissions to your
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 * Android Manifest file:</p>
127 * <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100128 *
Scott Main8b3cea02010-05-14 14:12:43 -0700129 * <p>This must be a child of the <a
Ben Dodson4e8620f2010-08-25 10:55:47 -0700130 * href="{@docRoot}guide/topics/manifest/manifest-element.html">{@code <manifest>}</a>
Scott Main8b3cea02010-05-14 14:12:43 -0700131 * element.</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100132 *
Scott Main41ec6532010-08-19 16:57:07 -0700133 * <p>See the <a href="{@docRoot}resources/tutorials/views/hello-webview.html">Web View
134 * tutorial</a>.</p>
135 *
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100136 * <h3>Basic usage</h3>
137 *
138 * <p>By default, a WebView provides no browser-like widgets, does not
Scott Main8b3cea02010-05-14 14:12:43 -0700139 * enable JavaScript and web page errors are ignored. If your goal is only
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100140 * to display some HTML as a part of your UI, this is probably fine;
141 * the user won't need to interact with the web page beyond reading
142 * it, and the web page won't need to interact with the user. If you
Scott Main8b3cea02010-05-14 14:12:43 -0700143 * actually want a full-blown web browser, then you probably want to
144 * invoke the Browser application with a URL Intent rather than show it
145 * with a WebView. For example:
146 * <pre>
147 * Uri uri = Uri.parse("http://www.example.com");
148 * Intent intent = new Intent(Intent.ACTION_VIEW, uri);
149 * startActivity(intent);
150 * </pre>
151 * <p>See {@link android.content.Intent} for more information.</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100152 *
Ben Dodson4e8620f2010-08-25 10:55:47 -0700153 * <p>To provide a WebView in your own Activity, include a {@code <WebView>} in your layout,
Scott Main8b3cea02010-05-14 14:12:43 -0700154 * or set the entire Activity window as a WebView during {@link
155 * android.app.Activity#onCreate(Bundle) onCreate()}:</p>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100156 * <pre class="prettyprint">
157 * WebView webview = new WebView(this);
158 * setContentView(webview);
Scott Main8b3cea02010-05-14 14:12:43 -0700159 * </pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100160 *
Scott Main8b3cea02010-05-14 14:12:43 -0700161 * <p>Then load the desired web page:</p>
Scott Maine3b9f8b2010-05-18 08:41:36 -0700162 * <pre>
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100163 * // Simplest usage: note that an exception will NOT be thrown
164 * // if there is an error loading this page (see below).
165 * webview.loadUrl("http://slashdot.org/");
166 *
Scott Main8b3cea02010-05-14 14:12:43 -0700167 * // OR, you can also load from an HTML string:
Scott Maine3b9f8b2010-05-18 08:41:36 -0700168 * String summary = "&lt;html>&lt;body>You scored &lt;b>192&lt;/b> points.&lt;/body>&lt;/html>";
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100169 * webview.loadData(summary, "text/html", "utf-8");
170 * // ... although note that there are restrictions on what this HTML can do.
Scott Main8b3cea02010-05-14 14:12:43 -0700171 * // See the JavaDocs for {@link #loadData(String,String,String) loadData()} and {@link
172 * #loadDataWithBaseURL(String,String,String,String,String) loadDataWithBaseURL()} for more info.
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100173 * </pre>
174 *
175 * <p>A WebView has several customization points where you can add your
176 * own behavior. These are:</p>
177 *
178 * <ul>
179 * <li>Creating and setting a {@link android.webkit.WebChromeClient} subclass.
180 * This class is called when something that might impact a
181 * browser UI happens, for instance, progress updates and
Scott Main8b3cea02010-05-14 14:12:43 -0700182 * JavaScript alerts are sent here (see <a
183 * href="{@docRoot}guide/developing/debug-tasks.html#DebuggingWebPages">Debugging Tasks</a>).
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100184 * </li>
185 * <li>Creating and setting a {@link android.webkit.WebViewClient} subclass.
186 * It will be called when things happen that impact the
187 * rendering of the content, eg, errors or form submissions. You
Scott Main8b3cea02010-05-14 14:12:43 -0700188 * can also intercept URL loading here (via {@link
189 * android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)
190 * shouldOverrideUrlLoading()}).</li>
191 * <li>Modifying the {@link android.webkit.WebSettings}, such as
192 * enabling JavaScript with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)
193 * setJavaScriptEnabled()}. </li>
194 * <li>Adding JavaScript-to-Java interfaces with the {@link
195 * android.webkit.WebView#addJavascriptInterface} method.
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100196 * This lets you bind Java objects into the WebView so they can be
197 * controlled from the web pages JavaScript.</li>
198 * </ul>
199 *
200 * <p>Here's a more complicated example, showing error handling,
201 * settings, and progress notification:</p>
202 *
203 * <pre class="prettyprint">
204 * // Let's display the progress in the activity title bar, like the
205 * // browser app does.
206 * getWindow().requestFeature(Window.FEATURE_PROGRESS);
207 *
208 * webview.getSettings().setJavaScriptEnabled(true);
209 *
210 * final Activity activity = this;
211 * webview.setWebChromeClient(new WebChromeClient() {
212 * public void onProgressChanged(WebView view, int progress) {
213 * // Activities and WebViews measure progress with different scales.
214 * // The progress meter will automatically disappear when we reach 100%
215 * activity.setProgress(progress * 1000);
216 * }
217 * });
218 * webview.setWebViewClient(new WebViewClient() {
219 * public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
220 * Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
221 * }
222 * });
223 *
224 * webview.loadUrl("http://slashdot.org/");
225 * </pre>
226 *
227 * <h3>Cookie and window management</h3>
228 *
229 * <p>For obvious security reasons, your application has its own
Scott Main8b3cea02010-05-14 14:12:43 -0700230 * cache, cookie store etc.&mdash;it does not share the Browser
231 * application's data. Cookies are managed on a separate thread, so
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100232 * operations like index building don't block the UI
233 * thread. Follow the instructions in {@link android.webkit.CookieSyncManager}
234 * if you want to use cookies in your application.
235 * </p>
236 *
237 * <p>By default, requests by the HTML to open new windows are
238 * ignored. This is true whether they be opened by JavaScript or by
239 * the target attribute on a link. You can customize your
Scott Main8b3cea02010-05-14 14:12:43 -0700240 * {@link WebChromeClient} to provide your own behaviour for opening multiple windows,
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100241 * and render them in whatever manner you want.</p>
242 *
Scott Main8b3cea02010-05-14 14:12:43 -0700243 * <p>The standard behavior for an Activity is to be destroyed and
244 * recreated when the device orientation or any other configuration changes. This will cause
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100245 * the WebView to reload the current page. If you don't want that, you
Scott Main8b3cea02010-05-14 14:12:43 -0700246 * can set your Activity to handle the {@code orientation} and {@code keyboardHidden}
Mike Hearnadcd2ed2009-01-21 16:44:36 +0100247 * changes, and then just leave the WebView alone. It'll automatically
Scott Main8b3cea02010-05-14 14:12:43 -0700248 * re-orient itself as appropriate. Read <a
249 * href="{@docRoot}guide/topics/resources/runtime-changes.html">Handling Runtime Changes</a> for
250 * more information about how to handle configuration changes during runtime.</p>
251 *
252 *
253 * <h3>Building web pages to support different screen densities</h3>
254 *
255 * <p>The screen density of a device is based on the screen resolution. A screen with low density
256 * has fewer available pixels per inch, where a screen with high density
Brad Fitzpatrick438d0592010-06-10 12:19:19 -0700257 * has more &mdash; sometimes significantly more &mdash; pixels per inch. The density of a
Scott Main8b3cea02010-05-14 14:12:43 -0700258 * screen is important because, other things being equal, a UI element (such as a button) whose
259 * height and width are defined in terms of screen pixels will appear larger on the lower density
260 * screen and smaller on the higher density screen.
261 * For simplicity, Android collapses all actual screen densities into three generalized densities:
262 * high, medium, and low.</p>
263 * <p>By default, WebView scales a web page so that it is drawn at a size that matches the default
264 * appearance on a medium density screen. So, it applies 1.5x scaling on a high density screen
265 * (because its pixels are smaller) and 0.75x scaling on a low density screen (because its pixels
266 * are bigger).
267 * Starting with API Level 5 (Android 2.0), WebView supports DOM, CSS, and meta tag features to help
268 * you (as a web developer) target screens with different screen densities.</p>
269 * <p>Here's a summary of the features you can use to handle different screen densities:</p>
270 * <ul>
271 * <li>The {@code window.devicePixelRatio} DOM property. The value of this property specifies the
272 * default scaling factor used for the current device. For example, if the value of {@code
273 * window.devicePixelRatio} is "1.0", then the device is considered a medium density (mdpi) device
274 * and default scaling is not applied to the web page; if the value is "1.5", then the device is
275 * considered a high density device (hdpi) and the page content is scaled 1.5x; if the
276 * value is "0.75", then the device is considered a low density device (ldpi) and the content is
277 * scaled 0.75x. However, if you specify the {@code "target-densitydpi"} meta property
278 * (discussed below), then you can stop this default scaling behavior.</li>
279 * <li>The {@code -webkit-device-pixel-ratio} CSS media query. Use this to specify the screen
280 * densities for which this style sheet is to be used. The corresponding value should be either
281 * "0.75", "1", or "1.5", to indicate that the styles are for devices with low density, medium
282 * density, or high density screens, respectively. For example:
283 * <pre>
284 * &lt;link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" /&gt;</pre>
285 * <p>The {@code hdpi.css} stylesheet is only used for devices with a screen pixel ration of 1.5,
286 * which is the high density pixel ratio.</p>
287 * </li>
288 * <li>The {@code target-densitydpi} property for the {@code viewport} meta tag. You can use
289 * this to specify the target density for which the web page is designed, using the following
290 * values:
291 * <ul>
292 * <li>{@code device-dpi} - Use the device's native dpi as the target dpi. Default scaling never
293 * occurs.</li>
294 * <li>{@code high-dpi} - Use hdpi as the target dpi. Medium and low density screens scale down
295 * as appropriate.</li>
296 * <li>{@code medium-dpi} - Use mdpi as the target dpi. High density screens scale up and
297 * low density screens scale down. This is also the default behavior.</li>
298 * <li>{@code low-dpi} - Use ldpi as the target dpi. Medium and high density screens scale up
299 * as appropriate.</li>
Ben Dodson4e8620f2010-08-25 10:55:47 -0700300 * <li><em>{@code <value>}</em> - Specify a dpi value to use as the target dpi (accepted
Scott Main8b3cea02010-05-14 14:12:43 -0700301 * values are 70-400).</li>
302 * </ul>
303 * <p>Here's an example meta tag to specify the target density:</p>
304 * <pre>&lt;meta name="viewport" content="target-densitydpi=device-dpi" /&gt;</pre></li>
305 * </ul>
306 * <p>If you want to modify your web page for different densities, by using the {@code
307 * -webkit-device-pixel-ratio} CSS media query and/or the {@code
308 * window.devicePixelRatio} DOM property, then you should set the {@code target-densitydpi} meta
309 * property to {@code device-dpi}. This stops Android from performing scaling in your web page and
310 * allows you to make the necessary adjustments for each density via CSS and JavaScript.</p>
311 *
Michael Kolb73980a92010-08-05 16:32:51 -0700312 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800313 */
Raphael30df2372010-03-06 10:09:54 -0800314@Widget
Cary Clarkd6982c92009-05-29 11:02:22 -0400315public class WebView extends AbsoluteLayout
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800316 implements ViewTreeObserver.OnGlobalFocusChangeListener,
317 ViewGroup.OnHierarchyChangeListener {
318
Teng-Hui Zhua7f76872010-11-29 11:15:32 -0800319 private class InnerGlobalLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
320 public void onGlobalLayout() {
321 if (isShown()) {
322 setGLRectViewport();
323 }
324 }
325 }
326
Teng-Hui Zhu55133372010-12-07 17:19:16 -0800327 private class InnerScrollChangedListener implements ViewTreeObserver.OnScrollChangedListener {
328 public void onScrollChanged() {
329 if (isShown()) {
330 setGLRectViewport();
331 }
332 }
333 }
334
Teng-Hui Zhua7f76872010-11-29 11:15:32 -0800335 // The listener to capture global layout change event.
Teng-Hui Zhu55133372010-12-07 17:19:16 -0800336 private InnerGlobalLayoutListener mGlobalLayoutListener = null;
337
338 // The listener to capture scroll event.
339 private InnerScrollChangedListener mScrollChangedListener = null;
Teng-Hui Zhua7f76872010-11-29 11:15:32 -0800340
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800341 // if AUTO_REDRAW_HACK is true, then the CALL key will toggle redrawing
342 // the screen all-the-time. Good for profiling our drawing code
343 static private final boolean AUTO_REDRAW_HACK = false;
344 // true means redraw the screen all-the-time. Only with AUTO_REDRAW_HACK
345 private boolean mAutoRedraw;
346
Mattias Falk0ae2ec82010-09-16 16:24:46 +0200347 // Reference to the AlertDialog displayed by InvokeListBox.
348 // It's used to dismiss the dialog in destroy if not done before.
349 private AlertDialog mListBoxDialog = null;
350
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 static final String LOGTAG = "webview";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800352
Derek Sollenberger90b6e482010-05-10 12:38:54 -0400353 private ZoomManager mZoomManager;
Cary Clarkd6982c92009-05-29 11:02:22 -0400354
Romain Guycabfcc12011-03-07 18:06:46 -0800355 private final Rect mGLRectViewport = new Rect();
356 private final Rect mViewRectViewport = new Rect();
Chet Haase91fc3cf2011-01-28 00:20:04 -0800357 private boolean mGLViewportEmpty = false;
Nicolas Roard12c18e62010-10-13 20:14:31 -0700358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 /**
360 * Transportation object for returning WebView across thread boundaries.
361 */
362 public class WebViewTransport {
363 private WebView mWebview;
364
365 /**
366 * Set the WebView to the transportation object.
367 * @param webview The WebView to transport.
368 */
369 public synchronized void setWebView(WebView webview) {
370 mWebview = webview;
371 }
372
373 /**
374 * Return the WebView object.
375 * @return WebView The transported WebView object.
376 */
377 public synchronized WebView getWebView() {
378 return mWebview;
379 }
380 }
381
382 // A final CallbackProxy shared by WebViewCore and BrowserFrame.
383 private final CallbackProxy mCallbackProxy;
384
385 private final WebViewDatabase mDatabase;
386
387 // SSL certificate for the main top-level page (if secure)
388 private SslCertificate mCertificate;
389
390 // Native WebView pointer that is 0 until the native object has been
391 // created.
392 private int mNativeClass;
393 // This would be final but it needs to be set to null when the WebView is
394 // destroyed.
395 private WebViewCore mWebViewCore;
396 // Handler for dispatching UI messages.
397 /* package */ final Handler mPrivateHandler = new PrivateHandler();
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400398 private WebTextView mWebTextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 // Used to ignore changes to webkit text that arrives to the UI side after
400 // more key events.
401 private int mTextGeneration;
402
Leon Scroggins IIIb24e18b2010-04-20 17:07:28 -0400403 /* package */ void incrementTextGeneration() { mTextGeneration++; }
404
Patrick Scott0a5ce012009-07-02 08:56:10 -0400405 // Used by WebViewCore to create child views.
406 /* package */ final ViewManager mViewManager;
407
Grace Kloba11438c32009-12-16 11:39:12 -0800408 // Used to display in full screen mode
409 PluginFullScreenHolder mFullScreenHolder;
410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 /**
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -0800412 * Position of the last touch event in pixels.
413 * Use integer to prevent loss of dragging delta calculation accuracy;
414 * which was done in float and converted to integer, and resulted in gradual
415 * and compounding touch position and view dragging mismatch.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 */
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -0800417 private int mLastTouchX;
418 private int mLastTouchY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419
420 /**
421 * Time of the last touch event.
422 */
423 private long mLastTouchTime;
424
425 /**
426 * Time of the last time sending touch event to WebViewCore
427 */
428 private long mLastSentTouchTime;
429
430 /**
431 * The minimum elapsed time before sending another ACTION_MOVE event to
Grace Kloba53d3c1e2009-06-26 11:36:03 -0700432 * WebViewCore. This really should be tuned for each type of the devices.
433 * For example in Google Map api test case, it takes Dream device at least
434 * 150ms to do a full cycle in the WebViewCore by processing a touch event,
435 * triggering the layout and drawing the picture. While the same process
436 * takes 60+ms on the current high speed device. If we make
437 * TOUCH_SENT_INTERVAL too small, there will be multiple touch events sent
438 * to WebViewCore queue and the real layout and draw events will be pushed
439 * to further, which slows down the refresh rate. Choose 50 to favor the
440 * current high speed devices. For Dream like devices, 100 is a better
441 * choice. Maybe make this in the buildspec later.
Huahui Wue2975f12010-12-14 13:45:28 -0800442 * (Update 12/14/2010: changed to 0 since current device should be able to
443 * handle the raw events and Map team voted to have the raw events too.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800444 */
Huahui Wue2975f12010-12-14 13:45:28 -0800445 private static final int TOUCH_SENT_INTERVAL = 0;
Ben Murdochecbc65c2010-01-13 10:54:56 +0000446 private int mCurrentTouchInterval = TOUCH_SENT_INTERVAL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447
448 /**
449 * Helper class to get velocity for fling
450 */
451 VelocityTracker mVelocityTracker;
Romain Guy4296fc42009-07-06 11:48:52 -0700452 private int mMaximumFling;
Cary Clark278ce052009-08-31 16:08:42 -0400453 private float mLastVelocity;
454 private float mLastVelX;
455 private float mLastVelY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800456
Patrick Scott62310912010-12-06 17:44:50 -0500457 // The id of the native layer being scrolled.
Patrick Scotta3ebcc92010-07-16 11:52:22 -0400458 private int mScrollingLayer;
Patrick Scott62310912010-12-06 17:44:50 -0500459 private Rect mScrollingLayerRect = new Rect();
Patrick Scotta3ebcc92010-07-16 11:52:22 -0400460
Grace Kloba3c19d992010-05-17 19:19:06 -0700461 // only trigger accelerated fling if the new velocity is at least
462 // MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION times of the previous velocity
463 private static final float MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION = 0.2f;
464
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800465 /**
466 * Touch mode
467 */
468 private int mTouchMode = TOUCH_DONE_MODE;
469 private static final int TOUCH_INIT_MODE = 1;
470 private static final int TOUCH_DRAG_START_MODE = 2;
471 private static final int TOUCH_DRAG_MODE = 3;
472 private static final int TOUCH_SHORTPRESS_START_MODE = 4;
473 private static final int TOUCH_SHORTPRESS_MODE = 5;
Grace Kloba04b28682009-09-14 14:38:37 -0700474 private static final int TOUCH_DOUBLE_TAP_MODE = 6;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800475 private static final int TOUCH_DONE_MODE = 7;
Cary Clark924af702010-06-04 16:37:43 -0400476 private static final int TOUCH_PINCH_DRAG = 8;
Patrick Scotta3ebcc92010-07-16 11:52:22 -0400477 private static final int TOUCH_DRAG_LAYER_MODE = 9;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478
479 // Whether to forward the touch events to WebCore
Huahui Wu5d4064c2011-01-21 16:10:23 -0800480 // Can only be set by WebKit via JNI.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800481 private boolean mForwardTouchEvents = false;
482
Grace Klobac2242f22010-03-05 14:00:26 -0800483 // Whether to prevent default during touch. The initial value depends on
484 // mForwardTouchEvents. If WebCore wants all the touch events, it says yes
485 // for touch down. Otherwise UI will wait for the answer of the first
486 // confirmed move before taking over the control.
487 private static final int PREVENT_DEFAULT_NO = 0;
488 private static final int PREVENT_DEFAULT_MAYBE_YES = 1;
489 private static final int PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN = 2;
490 private static final int PREVENT_DEFAULT_YES = 3;
491 private static final int PREVENT_DEFAULT_IGNORE = 4;
492 private int mPreventDefault = PREVENT_DEFAULT_IGNORE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800493
Grace Klobac2242f22010-03-05 14:00:26 -0800494 // true when the touch movement exceeds the slop
495 private boolean mConfirmMove;
Grace Kloba5f68d6f2009-12-08 18:42:54 -0800496
Grace Klobac2242f22010-03-05 14:00:26 -0800497 // if true, touch events will be first processed by WebCore, if prevent
498 // default is not set, the UI will continue handle them.
499 private boolean mDeferTouchProcess;
500
501 // to avoid interfering with the current touch events, track them
502 // separately. Currently no snapping or fling in the deferred process mode
503 private int mDeferTouchMode = TOUCH_DONE_MODE;
504 private float mLastDeferTouchX;
505 private float mLastDeferTouchY;
Grace Kloba5f68d6f2009-12-08 18:42:54 -0800506
Leon Scroggins72543e12009-07-23 15:29:45 -0400507 // To keep track of whether the current drag was initiated by a WebTextView,
508 // so that we know not to hide the cursor
509 boolean mDragFromTextInput;
510
Cary Clarkd6982c92009-05-29 11:02:22 -0400511 // Whether or not to draw the cursor ring.
512 private boolean mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800513
Mike Reedd205d5b2009-05-27 11:02:29 -0400514 // true if onPause has been called (and not onResume)
515 private boolean mIsPaused;
516
Cary Clarkb8491342010-11-29 16:23:19 -0500517 private HitTestResult mInitialHitTestResult;
518
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800519 /**
520 * Customizable constant
521 */
522 // pre-computed square of ViewConfiguration.getScaledTouchSlop()
523 private int mTouchSlopSquare;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700524 // pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
525 private int mDoubleTapSlopSquare;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -0700526 // pre-computed density adjusted navigation slop
527 private int mNavSlop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800528 // This should be ViewConfiguration.getTapTimeout()
529 // But system time out is 100ms, which is too short for the browser.
530 // In the browser, if it switches out of tap too soon, jump tap won't work.
531 private static final int TAP_TIMEOUT = 200;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800532 // This should be ViewConfiguration.getLongPressTimeout()
533 // But system time out is 500ms, which is too short for the browser.
534 // With a short timeout, it's difficult to treat trigger a short press.
535 private static final int LONG_PRESS_TIMEOUT = 1000;
536 // needed to avoid flinging after a pause of no movement
537 private static final int MIN_FLING_TIME = 250;
Cary Clark25415e22009-10-12 13:41:28 -0400538 // draw unfiltered after drag is held without movement
539 private static final int MOTIONLESS_TIME = 100;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800540 // The amount of content to overlap between two screens when going through
541 // pages with the space bar, in pixels.
542 private static final int PAGE_SCROLL_OVERLAP = 24;
543
544 /**
545 * These prevent calling requestLayout if either dimension is fixed. This
546 * depends on the layout parameters and the measure specs.
547 */
548 boolean mWidthCanMeasure;
549 boolean mHeightCanMeasure;
550
551 // Remember the last dimensions we sent to the native side so we can avoid
552 // sending the same dimensions more than once.
553 int mLastWidthSent;
554 int mLastHeightSent;
Shimeng (Simon) Wang48fc9092011-02-03 14:29:54 -0800555 // Since view height sent to webkit could be fixed to avoid relayout, this
556 // value records the last sent actual view height.
557 int mLastActualHeightSent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800558
559 private int mContentWidth; // cache of value from WebViewCore
560 private int mContentHeight; // cache of value from WebViewCore
561
Cary Clarkd6982c92009-05-29 11:02:22 -0400562 // Need to have the separate control for horizontal and vertical scrollbar
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800563 // style than the View's single scrollbar style
564 private boolean mOverlayHorizontalScrollbar = true;
565 private boolean mOverlayVerticalScrollbar = false;
566
567 // our standard speed. this way small distances will be traversed in less
568 // time than large distances, but we cap the duration, so that very large
569 // distances won't take too long to get there.
570 private static final int STD_SPEED = 480; // pixels per second
571 // time for the longest scroll animation
572 private static final int MAX_DURATION = 750; // milliseconds
Leon Scroggins03c87bf2009-09-18 15:05:59 -0400573 private static final int SLIDE_TITLE_DURATION = 500; // milliseconds
Adam Powell637d3372010-08-25 14:37:03 -0700574
575 // Used by OverScrollGlow
576 OverScroller mScroller;
577
578 private boolean mInOverScrollMode = false;
579 private static Paint mOverScrollBackground;
580 private static Paint mOverScrollBorder;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581
582 private boolean mWrapContent;
Cary Clark25415e22009-10-12 13:41:28 -0400583 private static final int MOTIONLESS_FALSE = 0;
584 private static final int MOTIONLESS_PENDING = 1;
585 private static final int MOTIONLESS_TRUE = 2;
Grace Kloba9b657802010-04-08 13:46:23 -0700586 private static final int MOTIONLESS_IGNORE = 3;
Cary Clark25415e22009-10-12 13:41:28 -0400587 private int mHeldMotionless;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588
Svetoslav Ganovda355512010-05-12 22:04:44 -0700589 // An instance for injecting accessibility in WebViews with disabled
590 // JavaScript or ones for which no accessibility script exists
591 private AccessibilityInjector mAccessibilityInjector;
592
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -0700593 // flag indicating if accessibility script is injected so we
594 // know to handle Shift and arrows natively first
595 private boolean mAccessibilityScriptInjected;
596
Grace Kloba178db412010-05-18 22:22:23 -0700597 // the color used to highlight the touch rectangles
598 private static final int mHightlightColor = 0x33000000;
599 // the round corner for the highlight path
600 private static final float TOUCH_HIGHLIGHT_ARC = 5.0f;
601 // the region indicating where the user touched on the screen
602 private Region mTouchHighlightRegion = new Region();
603 // the paint for the touch highlight
604 private Paint mTouchHightlightPaint;
605 // debug only
606 private static final boolean DEBUG_TOUCH_HIGHLIGHT = true;
607 private static final int TOUCH_HIGHLIGHT_ELAPSE_TIME = 2000;
608 private Paint mTouchCrossHairColor;
609 private int mTouchHighlightX;
610 private int mTouchHighlightY;
611
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -0800612 // Basically this proxy is used to tell the Video to update layer tree at
613 // SetBaseLayer time and to pause when WebView paused.
614 private HTML5VideoViewProxy mHTML5VideoViewProxy;
615
Grace Klobac2242f22010-03-05 14:00:26 -0800616 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 * Private message ids
618 */
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400619 private static final int REMEMBER_PASSWORD = 1;
620 private static final int NEVER_REMEMBER_PASSWORD = 2;
621 private static final int SWITCH_TO_SHORTPRESS = 3;
622 private static final int SWITCH_TO_LONGPRESS = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700623 private static final int RELEASE_SINGLE_TAP = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400624 private static final int REQUEST_FORM_DATA = 6;
Grace Kloba96949ef2010-01-25 09:53:01 -0800625 private static final int RESUME_WEBCORE_PRIORITY = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400626 private static final int DRAG_HELD_MOTIONLESS = 8;
627 private static final int AWAKEN_SCROLL_BARS = 9;
Grace Klobac2242f22010-03-05 14:00:26 -0800628 private static final int PREVENT_DEFAULT_TIMEOUT = 10;
Cary Clark6f5dfc62010-11-11 13:09:20 -0500629 private static final int SCROLL_SELECT_TEXT = 11;
630
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631
Grace Klobac2242f22010-03-05 14:00:26 -0800632 private static final int FIRST_PRIVATE_MSG_ID = REMEMBER_PASSWORD;
Cary Clark6f5dfc62010-11-11 13:09:20 -0500633 private static final int LAST_PRIVATE_MSG_ID = SCROLL_SELECT_TEXT;
Grace Klobac2242f22010-03-05 14:00:26 -0800634
635 /*
636 * Package message ids
637 */
Grace Klobac2242f22010-03-05 14:00:26 -0800638 static final int SCROLL_TO_MSG_ID = 101;
Grace Klobac2242f22010-03-05 14:00:26 -0800639 static final int NEW_PICTURE_MSG_ID = 105;
640 static final int UPDATE_TEXT_ENTRY_MSG_ID = 106;
641 static final int WEBCORE_INITIALIZED_MSG_ID = 107;
642 static final int UPDATE_TEXTFIELD_TEXT_MSG_ID = 108;
643 static final int UPDATE_ZOOM_RANGE = 109;
Leon Scroggins9ab32b62010-05-03 14:19:50 +0100644 static final int UNHANDLED_NAV_KEY = 110;
Grace Klobac2242f22010-03-05 14:00:26 -0800645 static final int CLEAR_TEXT_ENTRY = 111;
646 static final int UPDATE_TEXT_SELECTION_MSG_ID = 112;
647 static final int SHOW_RECT_MSG_ID = 113;
648 static final int LONG_PRESS_CENTER = 114;
649 static final int PREVENT_TOUCH_ID = 115;
650 static final int WEBCORE_NEED_TOUCH_EVENTS = 116;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 // obj=Rect in doc coordinates
Grace Klobac2242f22010-03-05 14:00:26 -0800652 static final int INVAL_RECT_MSG_ID = 117;
653 static final int REQUEST_KEYBOARD = 118;
654 static final int DO_MOTION_UP = 119;
655 static final int SHOW_FULLSCREEN = 120;
656 static final int HIDE_FULLSCREEN = 121;
657 static final int DOM_FOCUS_CHANGED = 122;
Grace Kloba8abd50b2010-07-08 15:02:14 -0700658 static final int REPLACE_BASE_CONTENT = 123;
Leon Scrogginsf2e17a82010-09-24 15:58:50 -0400659 static final int FORM_DID_BLUR = 124;
Grace Klobac2242f22010-03-05 14:00:26 -0800660 static final int RETURN_LABEL = 125;
661 static final int FIND_AGAIN = 126;
Grace Klobae8300a12010-03-12 13:32:55 -0800662 static final int CENTER_FIT_RECT = 127;
Leon Scrogginsb4157792010-03-18 12:42:33 -0400663 static final int REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID = 128;
Grace Kloba50004bc2010-04-13 22:58:51 -0700664 static final int SET_SCROLLBAR_MODES = 129;
Svetoslav Ganovda355512010-05-12 22:04:44 -0700665 static final int SELECTION_STRING_CHANGED = 130;
Grace Kloba178db412010-05-18 22:22:23 -0700666 static final int SET_TOUCH_HIGHLIGHT_RECTS = 131;
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -0700667 static final int SAVE_WEBARCHIVE_FINISHED = 132;
Cary Clarkd6982c92009-05-29 11:02:22 -0400668
Ben Murdoch62275a42010-09-07 11:27:28 +0100669 static final int SET_AUTOFILLABLE = 133;
Ben Murdoch961d55f2010-12-02 13:58:24 +0000670 static final int AUTOFILL_COMPLETE = 134;
Ben Murdoch62275a42010-09-07 11:27:28 +0100671
Svetoslav Ganov9504f572011-01-14 11:38:17 -0800672 static final int SELECT_AT = 135;
Derek Sollenbergerf3196cd2011-01-27 17:33:14 -0500673 static final int SCREEN_ON = 136;
Nicolas Roard0e778a12011-03-11 14:29:05 -0800674 static final int ENTER_FULLSCREEN_VIDEO = 137;
Svetoslav Ganov9504f572011-01-14 11:38:17 -0800675
Grace Klobac2242f22010-03-05 14:00:26 -0800676 private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
Grace Kloba178db412010-05-18 22:22:23 -0700677 private static final int LAST_PACKAGE_MSG_ID = SET_TOUCH_HIGHLIGHT_RECTS;
Grace Klobac2242f22010-03-05 14:00:26 -0800678
679 static final String[] HandlerPrivateDebugString = {
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400680 "REMEMBER_PASSWORD", // = 1;
681 "NEVER_REMEMBER_PASSWORD", // = 2;
682 "SWITCH_TO_SHORTPRESS", // = 3;
683 "SWITCH_TO_LONGPRESS", // = 4;
Grace Kloba8b97e4b2009-07-28 13:11:38 -0700684 "RELEASE_SINGLE_TAP", // = 5;
Leon Scrogginsd3465f62009-06-02 10:57:54 -0400685 "REQUEST_FORM_DATA", // = 6;
Grace Kloba96949ef2010-01-25 09:53:01 -0800686 "RESUME_WEBCORE_PRIORITY", // = 7;
Cary Clark25415e22009-10-12 13:41:28 -0400687 "DRAG_HELD_MOTIONLESS", // = 8;
688 "AWAKEN_SCROLL_BARS", // = 9;
Cary Clark6f5dfc62010-11-11 13:09:20 -0500689 "PREVENT_DEFAULT_TIMEOUT", // = 10;
690 "SCROLL_SELECT_TEXT" // = 11;
Grace Klobac2242f22010-03-05 14:00:26 -0800691 };
692
693 static final String[] HandlerPackageDebugString = {
694 "SCROLL_TO_MSG_ID", // = 101;
Patrick Scottfa8be1c2011-02-02 14:09:34 -0500695 "102", // = 102;
696 "103", // = 103;
697 "104", // = 104;
Grace Klobac2242f22010-03-05 14:00:26 -0800698 "NEW_PICTURE_MSG_ID", // = 105;
699 "UPDATE_TEXT_ENTRY_MSG_ID", // = 106;
700 "WEBCORE_INITIALIZED_MSG_ID", // = 107;
701 "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 108;
702 "UPDATE_ZOOM_RANGE", // = 109;
Leon Scroggins9ab32b62010-05-03 14:19:50 +0100703 "UNHANDLED_NAV_KEY", // = 110;
Grace Klobac2242f22010-03-05 14:00:26 -0800704 "CLEAR_TEXT_ENTRY", // = 111;
705 "UPDATE_TEXT_SELECTION_MSG_ID", // = 112;
706 "SHOW_RECT_MSG_ID", // = 113;
707 "LONG_PRESS_CENTER", // = 114;
708 "PREVENT_TOUCH_ID", // = 115;
709 "WEBCORE_NEED_TOUCH_EVENTS", // = 116;
710 "INVAL_RECT_MSG_ID", // = 117;
711 "REQUEST_KEYBOARD", // = 118;
712 "DO_MOTION_UP", // = 119;
713 "SHOW_FULLSCREEN", // = 120;
714 "HIDE_FULLSCREEN", // = 121;
715 "DOM_FOCUS_CHANGED", // = 122;
Grace Kloba8abd50b2010-07-08 15:02:14 -0700716 "REPLACE_BASE_CONTENT", // = 123;
Leon Scrogginsf2e17a82010-09-24 15:58:50 -0400717 "FORM_DID_BLUR", // = 124;
Grace Klobac2242f22010-03-05 14:00:26 -0800718 "RETURN_LABEL", // = 125;
Grace Klobae8300a12010-03-12 13:32:55 -0800719 "FIND_AGAIN", // = 126;
Leon Scrogginsb4157792010-03-18 12:42:33 -0400720 "CENTER_FIT_RECT", // = 127;
Grace Kloba50004bc2010-04-13 22:58:51 -0700721 "REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID", // = 128;
Grace Kloba178db412010-05-18 22:22:23 -0700722 "SET_SCROLLBAR_MODES", // = 129;
723 "SELECTION_STRING_CHANGED", // = 130;
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -0700724 "SET_TOUCH_HIGHLIGHT_RECTS", // = 131;
Ben Murdoch62275a42010-09-07 11:27:28 +0100725 "SAVE_WEBARCHIVE_FINISHED", // = 132;
Ben Murdoch961d55f2010-12-02 13:58:24 +0000726 "SET_AUTOFILLABLE", // = 133;
Svetoslav Ganov9504f572011-01-14 11:38:17 -0800727 "AUTOFILL_COMPLETE", // = 134;
Derek Sollenbergerf3196cd2011-01-27 17:33:14 -0500728 "SELECT_AT", // = 135;
Nicolas Roard0e778a12011-03-11 14:29:05 -0800729 "SCREEN_ON", // = 136;
730 "ENTER_FULLSCREEN_VIDEO" // = 137;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800731 };
732
Grace Klobaa4fa1072009-11-09 12:01:50 -0800733 // If the site doesn't use the viewport meta tag to specify the viewport,
734 // use DEFAULT_VIEWPORT_WIDTH as the default viewport width
Shimeng (Simon) Wang14bcc0e2010-09-17 10:12:05 -0700735 static final int DEFAULT_VIEWPORT_WIDTH = 980;
Grace Klobaa4fa1072009-11-09 12:01:50 -0800736
737 // normally we try to fit the content to the minimum preferred width
738 // calculated by the Webkit. To avoid the bad behavior when some site's
739 // minimum preferred width keeps growing when changing the viewport width or
740 // the minimum preferred width is huge, an upper limit is needed.
741 static int sMaxViewportWidth = DEFAULT_VIEWPORT_WIDTH;
742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 // initial scale in percent. 0 means using default.
Grace Kloba16efce72009-11-10 15:49:03 -0800744 private int mInitialScaleInPercent = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800745
Patrick Scottfa8be1c2011-02-02 14:09:34 -0500746 // Whether or not a scroll event should be sent to webkit. This is only set
747 // to false when restoring the scroll position.
748 private boolean mSendScrollEvent = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800749
750 private int mSnapScrollMode = SNAP_NONE;
Cary Clarkac492e12009-10-14 14:53:37 -0400751 private static final int SNAP_NONE = 0;
752 private static final int SNAP_LOCK = 1; // not a separate state
753 private static final int SNAP_X = 2; // may be combined with SNAP_LOCK
754 private static final int SNAP_Y = 4; // may be combined with SNAP_LOCK
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 private boolean mSnapPositive;
Cary Clarkd6982c92009-05-29 11:02:22 -0400756
Cary Clark2ec30692010-02-23 10:50:38 -0500757 // keep these in sync with their counterparts in WebView.cpp
758 private static final int DRAW_EXTRAS_NONE = 0;
759 private static final int DRAW_EXTRAS_FIND = 1;
760 private static final int DRAW_EXTRAS_SELECTION = 2;
761 private static final int DRAW_EXTRAS_CURSOR_RING = 3;
762
Grace Kloba50004bc2010-04-13 22:58:51 -0700763 // keep this in sync with WebCore:ScrollbarMode in WebKit
764 private static final int SCROLLBAR_AUTO = 0;
765 private static final int SCROLLBAR_ALWAYSOFF = 1;
766 // as we auto fade scrollbar, this is ignored.
767 private static final int SCROLLBAR_ALWAYSON = 2;
768 private int mHorizontalScrollBarMode = SCROLLBAR_AUTO;
769 private int mVerticalScrollBarMode = SCROLLBAR_AUTO;
770
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -0700771 // constants for determining script injection strategy
772 private static final int ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED = -1;
773 private static final int ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT = 0;
774 private static final int ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED = 1;
775
Svetoslav Ganovda355512010-05-12 22:04:44 -0700776 // the alias via which accessibility JavaScript interface is exposed
777 private static final String ALIAS_ACCESSIBILITY_JS_INTERFACE = "accessibility";
778
779 // JavaScript to inject the script chooser which will
780 // pick the right script for the current URL
781 private static final String ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT =
782 "javascript:(function() {" +
783 " var chooser = document.createElement('script');" +
784 " chooser.type = 'text/javascript';" +
785 " chooser.src = 'https://ssl.gstatic.com/accessibility/javascript/android/AndroidScriptChooser.user.js';" +
786 " document.getElementsByTagName('head')[0].appendChild(chooser);" +
787 " })();";
788
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -0700789 // Regular expression that matches the "axs" URL parameter.
790 // The value of 0 means the accessibility script is opted out
791 // The value of 1 means the accessibility script is already injected
792 private static final String PATTERN_MATCH_AXS_URL_PARAMETER = "(\\?axs=(0|1))|(&axs=(0|1))";
793
Svetoslav Ganov4acac232011-02-02 15:22:24 -0800794 // TextToSpeech instance exposed to JavaScript to the injected screenreader.
795 private TextToSpeech mTextToSpeech;
796
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -0700797 // variable to cache the above pattern in case accessibility is enabled.
798 private Pattern mMatchAxsUrlParameterPattern;
799
Adam Powell637d3372010-08-25 14:37:03 -0700800 /**
801 * Max distance to overscroll by in pixels.
802 * This how far content can be pulled beyond its normal bounds by the user.
803 */
804 private int mOverscrollDistance;
805
806 /**
807 * Max distance to overfling by in pixels.
808 * This is how far flinged content can move beyond the end of its normal bounds.
809 */
810 private int mOverflingDistance;
811
812 private OverScrollGlow mOverScrollGlow;
813
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 // Used to match key downs and key ups
Leon Scroggins115626a2011-02-17 12:00:48 -0500815 private Vector<Integer> mKeysPressed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800816
817 /* package */ static boolean mLogEvent = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818
819 // for event log
820 private long mLastTouchUpTime = 0;
821
Ben Murdochdb8d19c2010-10-29 11:44:17 +0100822 private WebViewCore.AutoFillData mAutoFillData;
Ben Murdoch62275a42010-09-07 11:27:28 +0100823
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 /**
825 * URI scheme for telephone number
826 */
827 public static final String SCHEME_TEL = "tel:";
828 /**
829 * URI scheme for email address
830 */
831 public static final String SCHEME_MAILTO = "mailto:";
832 /**
833 * URI scheme for map address
834 */
835 public static final String SCHEME_GEO = "geo:0,0?q=";
Cary Clarkd6982c92009-05-29 11:02:22 -0400836
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 private int mBackgroundColor = Color.WHITE;
838
Cary Clark6f5dfc62010-11-11 13:09:20 -0500839 private static final long SELECT_SCROLL_INTERVAL = 1000 / 60; // 60 / second
840 private int mAutoScrollX = 0;
841 private int mAutoScrollY = 0;
Cary Clarkb9aaa772011-01-07 16:14:54 -0500842 private int mMinAutoScrollX = 0;
843 private int mMaxAutoScrollX = 0;
844 private int mMinAutoScrollY = 0;
845 private int mMaxAutoScrollY = 0;
846 private Rect mScrollingLayerBounds = new Rect();
Cary Clark6f5dfc62010-11-11 13:09:20 -0500847 private boolean mSentAutoScrollMessage = false;
848
Adam Powell4fb35d42011-03-03 17:54:55 -0800849 // used for serializing asynchronously handled touch events.
850 private final TouchEventQueue mTouchEventQueue = new TouchEventQueue();
851
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 // Used to notify listeners of a new picture.
853 private PictureListener mPictureListener;
854 /**
855 * Interface to listen for new pictures as they change.
Kristian Monsen9f5f7af2011-02-24 11:14:11 +0000856 * @deprecated This interface is now obsolete.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 */
Kristian Monsen9f5f7af2011-02-24 11:14:11 +0000858 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 public interface PictureListener {
860 /**
861 * Notify the listener that the picture has changed.
862 * @param view The WebView that owns the picture.
863 * @param picture The new picture.
Kristian Monsen9f5f7af2011-02-24 11:14:11 +0000864 * @deprecated This method is now obsolete.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 */
Kristian Monsen9f5f7af2011-02-24 11:14:11 +0000866 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 public void onNewPicture(WebView view, Picture picture);
868 }
869
Leon Scroggins3246c222009-05-26 09:51:23 -0400870 // FIXME: Want to make this public, but need to change the API file.
871 public /*static*/ class HitTestResult {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 /**
873 * Default HitTestResult, where the target is unknown
874 */
875 public static final int UNKNOWN_TYPE = 0;
876 /**
877 * HitTestResult for hitting a HTML::a tag
878 */
879 public static final int ANCHOR_TYPE = 1;
880 /**
881 * HitTestResult for hitting a phone number
882 */
883 public static final int PHONE_TYPE = 2;
884 /**
885 * HitTestResult for hitting a map address
886 */
887 public static final int GEO_TYPE = 3;
888 /**
889 * HitTestResult for hitting an email address
890 */
891 public static final int EMAIL_TYPE = 4;
892 /**
893 * HitTestResult for hitting an HTML::img tag
894 */
895 public static final int IMAGE_TYPE = 5;
896 /**
897 * HitTestResult for hitting a HTML::a tag which contains HTML::img
898 */
899 public static final int IMAGE_ANCHOR_TYPE = 6;
900 /**
901 * HitTestResult for hitting a HTML::a tag with src=http
902 */
903 public static final int SRC_ANCHOR_TYPE = 7;
904 /**
905 * HitTestResult for hitting a HTML::a tag with src=http + HTML::img
906 */
907 public static final int SRC_IMAGE_ANCHOR_TYPE = 8;
908 /**
909 * HitTestResult for hitting an edit text area
910 */
911 public static final int EDIT_TEXT_TYPE = 9;
912
913 private int mType;
914 private String mExtra;
915
916 HitTestResult() {
917 mType = UNKNOWN_TYPE;
918 }
919
920 private void setType(int type) {
921 mType = type;
922 }
923
924 private void setExtra(String extra) {
925 mExtra = extra;
926 }
927
928 public int getType() {
929 return mType;
930 }
931
932 public String getExtra() {
933 return mExtra;
934 }
935 }
936
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 /**
938 * Construct a new WebView with a Context object.
939 * @param context A Context object used to access application assets.
940 */
941 public WebView(Context context) {
942 this(context, null);
943 }
944
945 /**
946 * Construct a new WebView with layout parameters.
947 * @param context A Context object used to access application assets.
948 * @param attrs An AttributeSet passed to our parent.
949 */
950 public WebView(Context context, AttributeSet attrs) {
951 this(context, attrs, com.android.internal.R.attr.webViewStyle);
952 }
953
954 /**
955 * Construct a new WebView with layout parameters and a default style.
956 * @param context A Context object used to access application assets.
957 * @param attrs An AttributeSet passed to our parent.
958 * @param defStyle The default style resource ID.
959 */
960 public WebView(Context context, AttributeSet attrs, int defStyle) {
Elliott Slaughterf21d2e32010-07-14 18:08:54 -0700961 this(context, attrs, defStyle, false);
962 }
963
964 /**
965 * Construct a new WebView with layout parameters and a default style.
966 * @param context A Context object used to access application assets.
967 * @param attrs An AttributeSet passed to our parent.
968 * @param defStyle The default style resource ID.
969 */
970 public WebView(Context context, AttributeSet attrs, int defStyle,
971 boolean privateBrowsing) {
972 this(context, attrs, defStyle, null, privateBrowsing);
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100973 }
974
975 /**
976 * Construct a new WebView with layout parameters, a default style and a set
977 * of custom Javscript interfaces to be added to the WebView at initialization
Romain Guy01d0fbf2009-12-01 14:52:19 -0800978 * time. This guarantees that these interfaces will be available when the JS
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100979 * context is initialized.
980 * @param context A Context object used to access application assets.
981 * @param attrs An AttributeSet passed to our parent.
982 * @param defStyle The default style resource ID.
Steve Block81f19ff2010-11-01 13:23:24 +0000983 * @param javaScriptInterfaces is a Map of interface names, as keys, and
Andrei Popescu4950b2b2009-09-03 13:56:07 +0100984 * object implementing those interfaces, as values.
985 * @hide pending API council approval.
986 */
987 protected WebView(Context context, AttributeSet attrs, int defStyle,
Steve Block81f19ff2010-11-01 13:23:24 +0000988 Map<String, Object> javaScriptInterfaces, boolean privateBrowsing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 super(context, attrs, defStyle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990
Kristian Monsend89a30a2010-11-16 18:11:59 +0000991 // Used by the chrome stack to find application paths
992 JniUtil.setContext(context);
993
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 mCallbackProxy = new CallbackProxy(context, this);
Grace Kloba9a67c822009-12-20 11:33:58 -0800995 mViewManager = new ViewManager(this);
Ben Murdochd2e91d12011-02-25 17:11:02 +0000996 L10nUtils.setApplicationContext(context.getApplicationContext());
Steve Block81f19ff2010-11-01 13:23:24 +0000997 mWebViewCore = new WebViewCore(context, this, mCallbackProxy, javaScriptInterfaces);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 mDatabase = WebViewDatabase.getInstance(context);
Adam Powell637d3372010-08-25 14:37:03 -0700999 mScroller = new OverScroller(context, null, 0, 0, false); //TODO Use OverScroller's flywheel
Derek Sollenberger03e48912010-05-18 17:03:42 -04001000 mZoomManager = new ZoomManager(this, mCallbackProxy);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001001
Derek Sollenberger90b6e482010-05-10 12:38:54 -04001002 /* The init method must follow the creation of certain member variables,
1003 * such as the mZoomManager.
1004 */
1005 init();
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001006 setupPackageListener(context);
Kristian Monsen41e7e6f2010-12-21 12:51:11 +00001007 setupProxyListener(context);
Grace Kloba3a0def22010-01-23 21:11:54 -08001008 updateMultiTouchSupport(context);
Elliott Slaughterf21d2e32010-07-14 18:08:54 -07001009
1010 if (privateBrowsing) {
1011 startPrivateBrowsing();
1012 }
Ben Murdochffcc49e2010-10-28 18:24:57 +01001013
Ben Murdoch01b04e12010-11-08 10:49:16 +00001014 mAutoFillData = new WebViewCore.AutoFillData();
Grace Kloba3a0def22010-01-23 21:11:54 -08001015 }
1016
Kristian Monsen41e7e6f2010-12-21 12:51:11 +00001017 private static class ProxyReceiver extends BroadcastReceiver {
1018 @Override
1019 public void onReceive(Context context, Intent intent) {
1020 if (intent.getAction().equals(Proxy.PROXY_CHANGE_ACTION)) {
1021 handleProxyBroadcast(intent);
1022 }
1023 }
1024 }
1025
Kristian Monsen9437d912010-12-23 15:39:31 +00001026 /*
1027 * A variable to track if there is a receiver added for PROXY_CHANGE_ACTION
1028 */
1029 private static boolean sProxyReceiverAdded;
1030
1031 private static synchronized void setupProxyListener(Context context) {
1032 if (sProxyReceiverAdded) {
1033 return;
1034 }
Kristian Monsen41e7e6f2010-12-21 12:51:11 +00001035 IntentFilter filter = new IntentFilter();
1036 filter.addAction(Proxy.PROXY_CHANGE_ACTION);
Kristian Monsen9437d912010-12-23 15:39:31 +00001037 Intent currentProxy = context.getApplicationContext().registerReceiver(
1038 new ProxyReceiver(), filter);
1039 sProxyReceiverAdded = true;
Kristian Monsen41e7e6f2010-12-21 12:51:11 +00001040 if (currentProxy != null) {
1041 handleProxyBroadcast(currentProxy);
1042 }
1043 }
1044
1045 private static void handleProxyBroadcast(Intent intent) {
1046 ProxyProperties proxyProperties = (ProxyProperties)intent.getExtra(Proxy.EXTRA_PROXY_INFO);
1047 if (proxyProperties == null || proxyProperties.getHost() == null) {
1048 WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, "");
1049 return;
1050 }
1051
1052 String host = proxyProperties.getHost();
1053 int port = proxyProperties.getPort();
1054 if (port != 0)
Kristian Monsen6f6b6402011-01-18 14:01:45 +00001055 host += ":" + port;
Kristian Monsen41e7e6f2010-12-21 12:51:11 +00001056
1057 // TODO: Handle exclusion list
1058 // The plan is to make an AndroidProxyResolver, and handle the blacklist
1059 // there
1060 String exclusionList = proxyProperties.getExclusionList();
1061 WebViewCore.sendStaticMessage(EventHub.PROXY_CHANGED, host);
1062 }
1063
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001064 /*
Kristian Monsen4190aab2010-12-16 18:39:21 +00001065 * A variable to track if there is a receiver added for ACTION_PACKAGE_ADDED
1066 * or ACTION_PACKAGE_REMOVED.
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001067 */
Kristian Monsen4190aab2010-12-16 18:39:21 +00001068 private static boolean sPackageInstallationReceiverAdded = false;
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001069
1070 /*
1071 * A set of Google packages we monitor for the
1072 * navigator.isApplicationInstalled() API. Add additional packages as
1073 * needed.
1074 */
1075 private static Set<String> sGoogleApps;
1076 static {
1077 sGoogleApps = new HashSet<String>();
1078 sGoogleApps.add("com.google.android.youtube");
1079 }
1080
Kristian Monsen4190aab2010-12-16 18:39:21 +00001081 private static class PackageListener extends BroadcastReceiver {
1082 @Override
1083 public void onReceive(Context context, Intent intent) {
1084 final String action = intent.getAction();
1085 final String packageName = intent.getData().getSchemeSpecificPart();
1086 final boolean replacing = intent.getBooleanExtra(Intent.EXTRA_REPLACING, false);
1087 if (Intent.ACTION_PACKAGE_REMOVED.equals(action) && replacing) {
1088 // if it is replacing, refreshPlugins() when adding
1089 return;
1090 }
1091
1092 if (sGoogleApps.contains(packageName)) {
1093 if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
1094 WebViewCore.sendStaticMessage(EventHub.ADD_PACKAGE_NAME, packageName);
1095 } else {
1096 WebViewCore.sendStaticMessage(EventHub.REMOVE_PACKAGE_NAME, packageName);
1097 }
1098 }
1099
1100 PluginManager pm = PluginManager.getInstance(context);
1101 if (pm.containsPluginPermissionAndSignatures(packageName)) {
1102 pm.refreshPlugins(Intent.ACTION_PACKAGE_ADDED.equals(action));
1103 }
1104 }
1105 }
1106
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001107 private void setupPackageListener(Context context) {
1108
1109 /*
1110 * we must synchronize the instance check and the creation of the
1111 * receiver to ensure that only ONE receiver exists for all WebView
1112 * instances.
1113 */
1114 synchronized (WebView.class) {
1115
1116 // if the receiver already exists then we do not need to register it
1117 // again
Kristian Monsen4190aab2010-12-16 18:39:21 +00001118 if (sPackageInstallationReceiverAdded) {
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001119 return;
1120 }
1121
1122 IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
1123 filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
1124 filter.addDataScheme("package");
Kristian Monsen4190aab2010-12-16 18:39:21 +00001125 BroadcastReceiver packageListener = new PackageListener();
1126 context.getApplicationContext().registerReceiver(packageListener, filter);
1127 sPackageInstallationReceiverAdded = true;
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001128 }
1129
1130 // check if any of the monitored apps are already installed
1131 AsyncTask<Void, Void, Set<String>> task = new AsyncTask<Void, Void, Set<String>>() {
1132
1133 @Override
1134 protected Set<String> doInBackground(Void... unused) {
1135 Set<String> installedPackages = new HashSet<String>();
1136 PackageManager pm = mContext.getPackageManager();
Kristian Monsen63a1b0c2011-03-09 13:54:54 +00001137 for (String name : sGoogleApps) {
1138 try {
1139 PackageInfo pInfo = pm.getPackageInfo(name,
1140 PackageManager.GET_ACTIVITIES | PackageManager.GET_SERVICES);
1141 installedPackages.add(name);
1142 } catch(PackageManager.NameNotFoundException e) {
1143 // package not found
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001144 }
1145 }
1146 return installedPackages;
1147 }
1148
1149 // Executes on the UI thread
1150 @Override
1151 protected void onPostExecute(Set<String> installedPackages) {
Derek Sollenbergerc92de672010-09-01 08:55:22 -04001152 if (mWebViewCore != null) {
1153 mWebViewCore.sendMessage(EventHub.ADD_PACKAGE_NAMES, installedPackages);
1154 }
Derek Sollenberger41d5e932010-08-23 14:51:41 -04001155 }
1156 };
1157 task.execute();
1158 }
1159
Grace Kloba3a0def22010-01-23 21:11:54 -08001160 void updateMultiTouchSupport(Context context) {
Derek Sollenberger293c3602010-06-04 10:44:48 -04001161 mZoomManager.updateMultiTouchSupport(context);
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001162 }
1163
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 private void init() {
1165 setWillNotDraw(false);
1166 setFocusable(true);
1167 setFocusableInTouchMode(true);
1168 setClickable(true);
1169 setLongClickable(true);
1170
Romain Guy4296fc42009-07-06 11:48:52 -07001171 final ViewConfiguration configuration = ViewConfiguration.get(getContext());
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001172 int slop = configuration.getScaledTouchSlop();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 mTouchSlopSquare = slop * slop;
1174 mMinLockSnapReverseDistance = slop;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07001175 slop = configuration.getScaledDoubleTapSlop();
1176 mDoubleTapSlopSquare = slop * slop;
Grace Kloba25737912009-06-19 12:42:47 -07001177 final float density = getContext().getResources().getDisplayMetrics().density;
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07001178 // use one line height, 16 based on our current default font, for how
1179 // far we allow a touch be away from the edge of a link
Grace Kloba25737912009-06-19 12:42:47 -07001180 mNavSlop = (int) (16 * density);
Derek Sollenberger90b6e482010-05-10 12:38:54 -04001181 mZoomManager.init(density);
Romain Guy4296fc42009-07-06 11:48:52 -07001182 mMaximumFling = configuration.getScaledMaximumFlingVelocity();
Patrick Scotta3ebcc92010-07-16 11:52:22 -04001183
1184 // Compute the inverse of the density squared.
1185 DRAG_LAYER_INVERSE_DENSITY_SQUARED = 1 / (density * density);
Adam Powell637d3372010-08-25 14:37:03 -07001186
1187 mOverscrollDistance = configuration.getScaledOverscrollDistance();
1188 mOverflingDistance = configuration.getScaledOverflingDistance();
Teng-Hui Zhu15bfa532011-02-14 17:01:16 -08001189
1190 setScrollBarStyle(super.getScrollBarStyle());
Leon Scroggins115626a2011-02-17 12:00:48 -05001191 // Initially use a size of two, since the user is likely to only hold
1192 // down two keys at a time (shift + another key)
1193 mKeysPressed = new Vector<Integer>(2);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -08001194 mHTML5VideoViewProxy = null ;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 }
1196
Svetoslav Ganovda355512010-05-12 22:04:44 -07001197 /**
Svetoslav Ganov4acac232011-02-02 15:22:24 -08001198 * Adds accessibility APIs to JavaScript.
Svetoslav Ganovda355512010-05-12 22:04:44 -07001199 *
Svetoslav Ganov4acac232011-02-02 15:22:24 -08001200 * Note: This method is responsible to performing the necessary
1201 * check if the accessibility APIs should be exposed.
Svetoslav Ganovda355512010-05-12 22:04:44 -07001202 */
Svetoslav Ganov4acac232011-02-02 15:22:24 -08001203 private void addAccessibilityApisToJavaScript() {
1204 if (AccessibilityManager.getInstance(mContext).isEnabled()
1205 && getSettings().getJavaScriptEnabled()) {
1206 // exposing the TTS for now ...
Michael Kolbc4ca53f2011-02-05 13:53:14 -08001207 mTextToSpeech = new TextToSpeech(getContext(), null);
Svetoslav Ganov4acac232011-02-02 15:22:24 -08001208 addJavascriptInterface(mTextToSpeech, ALIAS_ACCESSIBILITY_JS_INTERFACE);
Svetoslav Ganovda355512010-05-12 22:04:44 -07001209 }
Svetoslav Ganov4acac232011-02-02 15:22:24 -08001210 }
Svetoslav Ganovda355512010-05-12 22:04:44 -07001211
Svetoslav Ganov4acac232011-02-02 15:22:24 -08001212 /**
1213 * Removes accessibility APIs from JavaScript.
1214 */
1215 private void removeAccessibilityApisFromJavaScript() {
1216 // exposing the TTS for now ...
1217 if (mTextToSpeech != null) {
1218 removeJavascriptInterface(ALIAS_ACCESSIBILITY_JS_INTERFACE);
1219 mTextToSpeech.shutdown();
1220 mTextToSpeech = null;
1221 }
Svetoslav Ganovda355512010-05-12 22:04:44 -07001222 }
1223
Adam Powell637d3372010-08-25 14:37:03 -07001224 @Override
1225 public void setOverScrollMode(int mode) {
1226 super.setOverScrollMode(mode);
1227 if (mode != OVER_SCROLL_NEVER) {
1228 if (mOverScrollGlow == null) {
1229 mOverScrollGlow = new OverScrollGlow(this);
1230 }
1231 } else {
1232 mOverScrollGlow = null;
1233 }
1234 }
1235
Grace Kloba0d8b77c2009-06-25 11:20:51 -07001236 /* package */void updateDefaultZoomDensity(int zoomDensity) {
Derek Sollenberger03e48912010-05-18 17:03:42 -04001237 final float density = mContext.getResources().getDisplayMetrics().density
Grace Kloba0d8b77c2009-06-25 11:20:51 -07001238 * 100 / zoomDensity;
Derek Sollenberger03e48912010-05-18 17:03:42 -04001239 mNavSlop = (int) (16 * density);
1240 mZoomManager.updateDefaultZoomDensity(density);
Grace Kloba0d8b77c2009-06-25 11:20:51 -07001241 }
1242
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 /* package */ boolean onSavePassword(String schemePlusHost, String username,
1244 String password, final Message resumeMsg) {
1245 boolean rVal = false;
1246 if (resumeMsg == null) {
1247 // null resumeMsg implies saving password silently
1248 mDatabase.setUsernamePassword(schemePlusHost, username, password);
1249 } else {
1250 final Message remember = mPrivateHandler.obtainMessage(
1251 REMEMBER_PASSWORD);
1252 remember.getData().putString("host", schemePlusHost);
1253 remember.getData().putString("username", username);
1254 remember.getData().putString("password", password);
1255 remember.obj = resumeMsg;
1256
1257 final Message neverRemember = mPrivateHandler.obtainMessage(
1258 NEVER_REMEMBER_PASSWORD);
1259 neverRemember.getData().putString("host", schemePlusHost);
1260 neverRemember.getData().putString("username", username);
1261 neverRemember.getData().putString("password", password);
1262 neverRemember.obj = resumeMsg;
1263
1264 new AlertDialog.Builder(getContext())
1265 .setTitle(com.android.internal.R.string.save_password_label)
1266 .setMessage(com.android.internal.R.string.save_password_message)
1267 .setPositiveButton(com.android.internal.R.string.save_password_notnow,
1268 new DialogInterface.OnClickListener() {
1269 public void onClick(DialogInterface dialog, int which) {
1270 resumeMsg.sendToTarget();
1271 }
1272 })
1273 .setNeutralButton(com.android.internal.R.string.save_password_remember,
1274 new DialogInterface.OnClickListener() {
1275 public void onClick(DialogInterface dialog, int which) {
1276 remember.sendToTarget();
1277 }
1278 })
1279 .setNegativeButton(com.android.internal.R.string.save_password_never,
1280 new DialogInterface.OnClickListener() {
1281 public void onClick(DialogInterface dialog, int which) {
1282 neverRemember.sendToTarget();
1283 }
1284 })
1285 .setOnCancelListener(new OnCancelListener() {
1286 public void onCancel(DialogInterface dialog) {
1287 resumeMsg.sendToTarget();
1288 }
1289 }).show();
1290 // Return true so that WebViewCore will pause while the dialog is
1291 // up.
1292 rVal = true;
1293 }
1294 return rVal;
1295 }
1296
1297 @Override
1298 public void setScrollBarStyle(int style) {
1299 if (style == View.SCROLLBARS_INSIDE_INSET
1300 || style == View.SCROLLBARS_OUTSIDE_INSET) {
1301 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = false;
1302 } else {
1303 mOverlayHorizontalScrollbar = mOverlayVerticalScrollbar = true;
1304 }
1305 super.setScrollBarStyle(style);
1306 }
1307
1308 /**
1309 * Specify whether the horizontal scrollbar has overlay style.
1310 * @param overlay TRUE if horizontal scrollbar should have overlay style.
1311 */
1312 public void setHorizontalScrollbarOverlay(boolean overlay) {
1313 mOverlayHorizontalScrollbar = overlay;
1314 }
1315
1316 /**
1317 * Specify whether the vertical scrollbar has overlay style.
1318 * @param overlay TRUE if vertical scrollbar should have overlay style.
1319 */
1320 public void setVerticalScrollbarOverlay(boolean overlay) {
1321 mOverlayVerticalScrollbar = overlay;
1322 }
1323
1324 /**
1325 * Return whether horizontal scrollbar has overlay style
1326 * @return TRUE if horizontal scrollbar has overlay style.
1327 */
1328 public boolean overlayHorizontalScrollbar() {
1329 return mOverlayHorizontalScrollbar;
1330 }
1331
1332 /**
1333 * Return whether vertical scrollbar has overlay style
1334 * @return TRUE if vertical scrollbar has overlay style.
1335 */
1336 public boolean overlayVerticalScrollbar() {
1337 return mOverlayVerticalScrollbar;
1338 }
1339
1340 /*
1341 * Return the width of the view where the content of WebView should render
1342 * to.
Grace Kloba6ed525e2009-09-17 15:31:12 -07001343 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 */
Grace Kloba6ed525e2009-09-17 15:31:12 -07001345 /* package */ int getViewWidth() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001346 if (!isVerticalScrollBarEnabled() || mOverlayVerticalScrollbar) {
1347 return getWidth();
1348 } else {
Shimeng (Simon) Wang37127542010-12-03 16:34:00 -08001349 return Math.max(0, getWidth() - getVerticalScrollbarWidth());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 }
1351 }
1352
Michael Kolb601e7792011-02-23 17:43:16 -08001353 /**
Mike Reede8853fc2009-09-04 14:01:48 -04001354 * returns the height of the titlebarview (if any). Does not care about
1355 * scrolling
Michael Kolb75a03f92011-02-23 16:12:13 -08001356 * @hide
Mike Reede8853fc2009-09-04 14:01:48 -04001357 */
Michael Kolb75a03f92011-02-23 16:12:13 -08001358 protected int getTitleHeight() {
Mike Reede8853fc2009-09-04 14:01:48 -04001359 return mTitleBar != null ? mTitleBar.getHeight() : 0;
1360 }
1361
Michael Kolbe54f6652011-03-16 09:11:51 -07001362 /**
Mike Reede8853fc2009-09-04 14:01:48 -04001363 * Return the amount of the titlebarview (if any) that is visible
Michael Kolb73980a92010-08-05 16:32:51 -07001364 *
Michael Kolb24e53b02011-03-16 12:52:04 -07001365 * @deprecated This method is now obsolete.
Mike Reede8853fc2009-09-04 14:01:48 -04001366 */
Michael Kolb73980a92010-08-05 16:32:51 -07001367 public int getVisibleTitleHeight() {
Adam Powell637d3372010-08-25 14:37:03 -07001368 // need to restrict mScrollY due to over scroll
1369 return Math.max(getTitleHeight() - Math.max(0, mScrollY), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04001370 }
1371
1372 /*
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 * Return the height of the view where the content of WebView should render
Leon Scroggins0236e672009-09-02 21:12:08 -04001374 * to. Note that this excludes mTitleBar, if there is one.
Grace Kloba6ed525e2009-09-17 15:31:12 -07001375 * Note: this can be called from WebCoreThread.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001376 */
Grace Kloba6ed525e2009-09-17 15:31:12 -07001377 /* package */ int getViewHeight() {
Grace Kloba8eff73f2009-09-24 09:34:32 -07001378 return getViewHeightWithTitle() - getVisibleTitleHeight();
1379 }
1380
1381 private int getViewHeightWithTitle() {
Leon Scroggins0236e672009-09-02 21:12:08 -04001382 int height = getHeight();
Mike Reede8853fc2009-09-04 14:01:48 -04001383 if (isHorizontalScrollBarEnabled() && !mOverlayHorizontalScrollbar) {
Leon Scroggins0236e672009-09-02 21:12:08 -04001384 height -= getHorizontalScrollbarHeight();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 }
Grace Kloba8eff73f2009-09-24 09:34:32 -07001386 return height;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001387 }
1388
1389 /**
1390 * @return The SSL certificate for the main top-level page or null if
1391 * there is no certificate (the site is not secure).
1392 */
1393 public SslCertificate getCertificate() {
1394 return mCertificate;
1395 }
1396
1397 /**
1398 * Sets the SSL certificate for the main top-level page.
1399 */
1400 public void setCertificate(SslCertificate certificate) {
Brian Carlstromdba8cb72010-03-18 16:56:41 -07001401 if (DebugFlags.WEB_VIEW) {
1402 Log.v(LOGTAG, "setCertificate=" + certificate);
1403 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001404 // here, the certificate can be null (if the site is not secure)
1405 mCertificate = certificate;
1406 }
1407
1408 //-------------------------------------------------------------------------
1409 // Methods called by activity
1410 //-------------------------------------------------------------------------
1411
1412 /**
1413 * Save the username and password for a particular host in the WebView's
1414 * internal database.
1415 * @param host The host that required the credentials.
1416 * @param username The username for the given host.
1417 * @param password The password for the given host.
1418 */
1419 public void savePassword(String host, String username, String password) {
1420 mDatabase.setUsernamePassword(host, username, password);
1421 }
1422
1423 /**
1424 * Set the HTTP authentication credentials for a given host and realm.
1425 *
1426 * @param host The host for the credentials.
1427 * @param realm The realm for the credentials.
1428 * @param username The username for the password. If it is null, it means
1429 * password can't be saved.
1430 * @param password The password
1431 */
1432 public void setHttpAuthUsernamePassword(String host, String realm,
1433 String username, String password) {
1434 mDatabase.setHttpAuthUsernamePassword(host, realm, username, password);
1435 }
1436
1437 /**
1438 * Retrieve the HTTP authentication username and password for a given
1439 * host & realm pair
1440 *
1441 * @param host The host for which the credentials apply.
1442 * @param realm The realm for which the credentials apply.
1443 * @return String[] if found, String[0] is username, which can be null and
1444 * String[1] is password. Return null if it can't find anything.
1445 */
1446 public String[] getHttpAuthUsernamePassword(String host, String realm) {
1447 return mDatabase.getHttpAuthUsernamePassword(host, realm);
1448 }
1449
1450 /**
Leon Scroggins05919f22010-09-14 17:22:36 -04001451 * Remove Find or Select ActionModes, if active.
1452 */
1453 private void clearActionModes() {
1454 if (mSelectCallback != null) {
1455 mSelectCallback.finish();
1456 }
1457 if (mFindCallback != null) {
1458 mFindCallback.finish();
1459 }
1460 }
1461
1462 /**
1463 * Called to clear state when moving from one page to another, or changing
1464 * in some other way that makes elements associated with the current page
1465 * (such as WebTextView or ActionModes) no longer relevant.
1466 */
1467 private void clearHelpers() {
1468 clearTextEntry();
1469 clearActionModes();
Derek Sollenbergerfa89c502010-10-01 11:19:36 -04001470 dismissFullScreenMode();
Leon Scroggins05919f22010-09-14 17:22:36 -04001471 }
1472
1473 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001474 * Destroy the internal state of the WebView. This method should be called
1475 * after the WebView has been removed from the view system. No other
1476 * methods may be called on a WebView after destroy.
1477 */
1478 public void destroy() {
Leon Scroggins05919f22010-09-14 17:22:36 -04001479 clearHelpers();
Mattias Falk0ae2ec82010-09-16 16:24:46 +02001480 if (mListBoxDialog != null) {
1481 mListBoxDialog.dismiss();
1482 mListBoxDialog = null;
1483 }
Cary Clarkd9fd8572011-02-03 05:25:05 -05001484 if (mNativeClass != 0) nativeStopGL();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001485 if (mWebViewCore != null) {
1486 // Set the handlers to null before destroying WebViewCore so no
Cary Clarkd6982c92009-05-29 11:02:22 -04001487 // more messages will be posted.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001488 mCallbackProxy.setWebViewClient(null);
1489 mCallbackProxy.setWebChromeClient(null);
1490 // Tell WebViewCore to destroy itself
Andrei Popescu04098572010-03-09 12:23:19 +00001491 synchronized (this) {
1492 WebViewCore webViewCore = mWebViewCore;
1493 mWebViewCore = null; // prevent using partial webViewCore
1494 webViewCore.destroy();
1495 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001496 // Remove any pending messages that might not be serviced yet.
1497 mPrivateHandler.removeCallbacksAndMessages(null);
1498 mCallbackProxy.removeCallbacksAndMessages(null);
1499 // Wake up the WebCore thread just in case it is waiting for a
Steve Block81f19ff2010-11-01 13:23:24 +00001500 // JavaScript dialog.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001501 synchronized (mCallbackProxy) {
1502 mCallbackProxy.notify();
1503 }
1504 }
1505 if (mNativeClass != 0) {
1506 nativeDestroy();
1507 mNativeClass = 0;
1508 }
1509 }
1510
1511 /**
1512 * Enables platform notifications of data state and proxy changes.
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00001513 * @deprecated Obsolete - platform notifications are always enabled.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001514 */
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00001515 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001516 public static void enablePlatformNotifications() {
1517 Network.enablePlatformNotifications();
1518 }
1519
1520 /**
1521 * If platform notifications are enabled, this should be called
Mike Reedd205d5b2009-05-27 11:02:29 -04001522 * from the Activity's onPause() or onStop().
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00001523 * @deprecated Obsolete - platform notifications are always enabled.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001524 */
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00001525 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001526 public static void disablePlatformNotifications() {
1527 Network.disablePlatformNotifications();
1528 }
Cary Clarkd6982c92009-05-29 11:02:22 -04001529
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001530 /**
Feng Qianb3081372009-06-29 15:55:18 -07001531 * Sets JavaScript engine flags.
1532 *
1533 * @param flags JS engine flags in a String
1534 *
1535 * @hide pending API solidification
1536 */
1537 public void setJsFlags(String flags) {
1538 mWebViewCore.sendMessage(EventHub.SET_JS_FLAGS, flags);
1539 }
1540
1541 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001542 * Inform WebView of the network state. This is used to set
Steve Block81f19ff2010-11-01 13:23:24 +00001543 * the JavaScript property window.navigator.isOnline and
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001544 * generates the online/offline event as specified in HTML5, sec. 5.7.7
1545 * @param networkUp boolean indicating if network is available
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001546 */
1547 public void setNetworkAvailable(boolean networkUp) {
Grace Klobaa72cc092009-04-02 08:50:17 -07001548 mWebViewCore.sendMessage(EventHub.SET_NETWORK_STATE,
1549 networkUp ? 1 : 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001550 }
1551
1552 /**
Andrei Popescuf5dba882010-01-12 22:42:41 +00001553 * Inform WebView about the current network type.
1554 * {@hide}
1555 */
1556 public void setNetworkType(String type, String subtype) {
1557 Map<String, String> map = new HashMap<String, String>();
1558 map.put("type", type);
1559 map.put("subtype", subtype);
1560 mWebViewCore.sendMessage(EventHub.SET_NETWORK_TYPE, map);
1561 }
1562 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04001563 * Save the state of this WebView used in
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001564 * {@link android.app.Activity#onSaveInstanceState}. Please note that this
1565 * method no longer stores the display data for this WebView. The previous
1566 * behavior could potentially leak files if {@link #restoreState} was never
1567 * called. See {@link #savePicture} and {@link #restorePicture} for saving
1568 * and restoring the display data.
1569 * @param outState The Bundle to store the WebView state.
1570 * @return The same copy of the back/forward list used to save the state. If
1571 * saveState fails, the returned list will be null.
1572 * @see #savePicture
1573 * @see #restorePicture
1574 */
1575 public WebBackForwardList saveState(Bundle outState) {
1576 if (outState == null) {
1577 return null;
1578 }
1579 // We grab a copy of the back/forward list because a client of WebView
1580 // may have invalidated the history list by calling clearHistory.
1581 WebBackForwardList list = copyBackForwardList();
1582 final int currentIndex = list.getCurrentIndex();
1583 final int size = list.getSize();
1584 // We should fail saving the state if the list is empty or the index is
1585 // not in a valid range.
1586 if (currentIndex < 0 || currentIndex >= size || size == 0) {
1587 return null;
1588 }
1589 outState.putInt("index", currentIndex);
1590 // FIXME: This should just be a byte[][] instead of ArrayList but
1591 // Parcel.java does not have the code to handle multi-dimensional
1592 // arrays.
1593 ArrayList<byte[]> history = new ArrayList<byte[]>(size);
1594 for (int i = 0; i < size; i++) {
1595 WebHistoryItem item = list.getItemAtIndex(i);
Cary Clark4fbf81b2009-09-29 16:07:56 -04001596 if (null == item) {
1597 // FIXME: this shouldn't happen
1598 // need to determine how item got set to null
1599 Log.w(LOGTAG, "saveState: Unexpected null history item.");
1600 return null;
1601 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001602 byte[] data = item.getFlattenedData();
1603 if (data == null) {
1604 // It would be very odd to not have any data for a given history
1605 // item. And we will fail to rebuild the history list without
1606 // flattened data.
1607 return null;
1608 }
1609 history.add(data);
1610 }
1611 outState.putSerializable("history", history);
1612 if (mCertificate != null) {
1613 outState.putBundle("certificate",
1614 SslCertificate.saveState(mCertificate));
1615 }
Elliott Slaughterd6284792010-08-18 18:17:52 -07001616 outState.putBoolean("privateBrowsingEnabled", isPrivateBrowsingEnabled());
Shimeng (Simon) Wang4ae1f6f2010-08-26 10:58:38 -07001617 mZoomManager.saveZoomState(outState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001618 return list;
1619 }
1620
1621 /**
1622 * Save the current display data to the Bundle given. Used in conjunction
1623 * with {@link #saveState}.
1624 * @param b A Bundle to store the display data.
1625 * @param dest The file to store the serialized picture data. Will be
1626 * overwritten with this WebView's picture data.
1627 * @return True if the picture was successfully saved.
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00001628 * @deprecated This method is now obsolete.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001629 */
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00001630 @Deprecated
Patrick Scottda9a22b2010-04-08 08:32:52 -04001631 public boolean savePicture(Bundle b, final File dest) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001632 if (dest == null || b == null) {
1633 return false;
1634 }
1635 final Picture p = capturePicture();
Patrick Scottda9a22b2010-04-08 08:32:52 -04001636 // Use a temporary file while writing to ensure the destination file
1637 // contains valid data.
1638 final File temp = new File(dest.getPath() + ".writing");
1639 new Thread(new Runnable() {
1640 public void run() {
The Android Open Source Project2d743fe2010-06-04 11:07:49 -07001641 FileOutputStream out = null;
Patrick Scottda9a22b2010-04-08 08:32:52 -04001642 try {
The Android Open Source Project2d743fe2010-06-04 11:07:49 -07001643 out = new FileOutputStream(temp);
Patrick Scottda9a22b2010-04-08 08:32:52 -04001644 p.writeToStream(out);
Patrick Scottda9a22b2010-04-08 08:32:52 -04001645 // Writing the picture succeeded, rename the temporary file
1646 // to the destination.
1647 temp.renameTo(dest);
1648 } catch (Exception e) {
1649 // too late to do anything about it.
1650 } finally {
The Android Open Source Project2d743fe2010-06-04 11:07:49 -07001651 if (out != null) {
1652 try {
1653 out.close();
1654 } catch (Exception e) {
1655 // Can't do anything about that
1656 }
1657 }
Patrick Scottda9a22b2010-04-08 08:32:52 -04001658 temp.delete();
1659 }
1660 }
1661 }).start();
1662 // now update the bundle
1663 b.putInt("scrollX", mScrollX);
1664 b.putInt("scrollY", mScrollY);
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04001665 mZoomManager.saveZoomState(b);
Patrick Scottda9a22b2010-04-08 08:32:52 -04001666 return true;
1667 }
1668
1669 private void restoreHistoryPictureFields(Picture p, Bundle b) {
1670 int sx = b.getInt("scrollX", 0);
1671 int sy = b.getInt("scrollY", 0);
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04001672
Patrick Scottda9a22b2010-04-08 08:32:52 -04001673 mDrawHistory = true;
1674 mHistoryPicture = p;
Huahui Wu88b869a2011-03-17 17:42:12 -07001675
Patrick Scottda9a22b2010-04-08 08:32:52 -04001676 mScrollX = sx;
1677 mScrollY = sy;
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04001678 mZoomManager.restoreZoomState(b);
1679 final float scale = mZoomManager.getScale();
Patrick Scottda9a22b2010-04-08 08:32:52 -04001680 mHistoryWidth = Math.round(p.getWidth() * scale);
1681 mHistoryHeight = Math.round(p.getHeight() * scale);
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04001682
Patrick Scottda9a22b2010-04-08 08:32:52 -04001683 invalidate();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001684 }
1685
1686 /**
1687 * Restore the display data that was save in {@link #savePicture}. Used in
1688 * conjunction with {@link #restoreState}.
1689 * @param b A Bundle containing the saved display data.
1690 * @param src The file where the picture data was stored.
1691 * @return True if the picture was successfully restored.
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00001692 * @deprecated This method is now obsolete.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001693 */
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00001694 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001695 public boolean restorePicture(Bundle b, File src) {
1696 if (src == null || b == null) {
1697 return false;
1698 }
Patrick Scottda9a22b2010-04-08 08:32:52 -04001699 if (!src.exists()) {
1700 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001701 }
Patrick Scottda9a22b2010-04-08 08:32:52 -04001702 try {
1703 final FileInputStream in = new FileInputStream(src);
1704 final Bundle copy = new Bundle(b);
1705 new Thread(new Runnable() {
1706 public void run() {
Patrick Scottda9a22b2010-04-08 08:32:52 -04001707 try {
The Android Open Source Project2d743fe2010-06-04 11:07:49 -07001708 final Picture p = Picture.createFromStream(in);
1709 if (p != null) {
1710 // Post a runnable on the main thread to update the
1711 // history picture fields.
1712 mPrivateHandler.post(new Runnable() {
1713 public void run() {
1714 restoreHistoryPictureFields(p, copy);
1715 }
1716 });
1717 }
1718 } finally {
1719 try {
1720 in.close();
1721 } catch (Exception e) {
1722 // Nothing we can do now.
1723 }
Patrick Scottda9a22b2010-04-08 08:32:52 -04001724 }
1725 }
1726 }).start();
1727 } catch (FileNotFoundException e){
1728 e.printStackTrace();
1729 }
1730 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001731 }
1732
1733 /**
1734 * Restore the state of this WebView from the given map used in
Cary Clarkd6982c92009-05-29 11:02:22 -04001735 * {@link android.app.Activity#onRestoreInstanceState}. This method should
1736 * be called to restore the state of the WebView before using the object. If
1737 * it is called after the WebView has had a chance to build state (load
1738 * pages, create a back/forward list, etc.) there may be undesirable
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001739 * side-effects. Please note that this method no longer restores the
1740 * display data for this WebView. See {@link #savePicture} and {@link
1741 * #restorePicture} for saving and restoring the display data.
1742 * @param inState The incoming Bundle of state.
1743 * @return The restored back/forward list or null if restoreState failed.
1744 * @see #savePicture
1745 * @see #restorePicture
1746 */
1747 public WebBackForwardList restoreState(Bundle inState) {
1748 WebBackForwardList returnList = null;
1749 if (inState == null) {
1750 return returnList;
1751 }
1752 if (inState.containsKey("index") && inState.containsKey("history")) {
1753 mCertificate = SslCertificate.restoreState(
1754 inState.getBundle("certificate"));
1755
1756 final WebBackForwardList list = mCallbackProxy.getBackForwardList();
1757 final int index = inState.getInt("index");
1758 // We can't use a clone of the list because we need to modify the
1759 // shared copy, so synchronize instead to prevent concurrent
1760 // modifications.
1761 synchronized (list) {
1762 final List<byte[]> history =
1763 (List<byte[]>) inState.getSerializable("history");
1764 final int size = history.size();
1765 // Check the index bounds so we don't crash in native code while
1766 // restoring the history index.
1767 if (index < 0 || index >= size) {
1768 return null;
1769 }
1770 for (int i = 0; i < size; i++) {
1771 byte[] data = history.remove(0);
1772 if (data == null) {
1773 // If we somehow have null data, we cannot reconstruct
1774 // the item and thus our history list cannot be rebuilt.
1775 return null;
1776 }
1777 WebHistoryItem item = new WebHistoryItem(data);
1778 list.addHistoryItem(item);
1779 }
1780 // Grab the most recent copy to return to the caller.
1781 returnList = copyBackForwardList();
1782 // Update the copy to have the correct index.
1783 returnList.setCurrentIndex(index);
1784 }
Elliott Slaughterd6284792010-08-18 18:17:52 -07001785 // Restore private browsing setting.
1786 if (inState.getBoolean("privateBrowsingEnabled")) {
1787 getSettings().setPrivateBrowsingEnabled(true);
1788 }
Shimeng (Simon) Wang4ae1f6f2010-08-26 10:58:38 -07001789 mZoomManager.restoreZoomState(inState);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001790 // Remove all pending messages because we are restoring previous
1791 // state.
1792 mWebViewCore.removeMessages();
1793 // Send a restore state message.
1794 mWebViewCore.sendMessage(EventHub.RESTORE_STATE, index);
1795 }
1796 return returnList;
1797 }
1798
1799 /**
Grace Klobad0d9bc22010-01-26 18:08:28 -08001800 * Load the given url with the extra headers.
1801 * @param url The url of the resource to load.
1802 * @param extraHeaders The extra headers sent with this url. This should not
1803 * include the common headers like "user-agent". If it does, it
1804 * will be replaced by the intrinsic value of the WebView.
1805 */
1806 public void loadUrl(String url, Map<String, String> extraHeaders) {
1807 switchOutDrawHistory();
1808 WebViewCore.GetUrlData arg = new WebViewCore.GetUrlData();
1809 arg.mUrl = url;
1810 arg.mExtraHeaders = extraHeaders;
1811 mWebViewCore.sendMessage(EventHub.LOAD_URL, arg);
Leon Scroggins05919f22010-09-14 17:22:36 -04001812 clearHelpers();
Grace Klobad0d9bc22010-01-26 18:08:28 -08001813 }
1814
1815 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001816 * Load the given url.
1817 * @param url The url of the resource to load.
1818 */
1819 public void loadUrl(String url) {
Patrick Scott4c3ca702009-07-15 15:41:05 -04001820 if (url == null) {
1821 return;
1822 }
Grace Klobad0d9bc22010-01-26 18:08:28 -08001823 loadUrl(url, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001824 }
1825
1826 /**
Grace Kloba57534302009-05-22 18:55:02 -07001827 * Load the url with postData using "POST" method into the WebView. If url
1828 * is not a network url, it will be loaded with {link
1829 * {@link #loadUrl(String)} instead.
Cary Clarkd6982c92009-05-29 11:02:22 -04001830 *
Grace Kloba57534302009-05-22 18:55:02 -07001831 * @param url The url of the resource to load.
1832 * @param postData The data will be passed to "POST" request.
Grace Kloba57534302009-05-22 18:55:02 -07001833 */
1834 public void postUrl(String url, byte[] postData) {
1835 if (URLUtil.isNetworkUrl(url)) {
1836 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001837 WebViewCore.PostUrlData arg = new WebViewCore.PostUrlData();
1838 arg.mUrl = url;
1839 arg.mPostData = postData;
Grace Kloba57534302009-05-22 18:55:02 -07001840 mWebViewCore.sendMessage(EventHub.POST_URL, arg);
Leon Scroggins05919f22010-09-14 17:22:36 -04001841 clearHelpers();
Grace Kloba57534302009-05-22 18:55:02 -07001842 } else {
1843 loadUrl(url);
1844 }
1845 }
1846
1847 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001848 * Load the given data into the WebView. This will load the data into
1849 * WebView using the data: scheme. Content loaded through this mechanism
1850 * does not have the ability to load content from the network.
Cary Clarkfc2ece42010-03-15 16:01:49 -04001851 * @param data A String of data in the given encoding. The date must
1852 * be URI-escaped -- '#', '%', '\', '?' should be replaced by %23, %25,
1853 * %27, %3f respectively.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001854 * @param mimeType The MIMEType of the data. i.e. text/html, image/jpeg
1855 * @param encoding The encoding of the data. i.e. utf-8, base64
1856 */
1857 public void loadData(String data, String mimeType, String encoding) {
1858 loadUrl("data:" + mimeType + ";" + encoding + "," + data);
1859 }
1860
1861 /**
1862 * Load the given data into the WebView, use the provided URL as the base
1863 * URL for the content. The base URL is the URL that represents the page
Leon Scroggins1bb1a912010-03-23 15:39:46 -04001864 * that is loaded through this interface. As such, it is used to resolve any
1865 * relative URLs. The historyUrl is used for the history entry.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001866 * <p>
1867 * Note for post 1.0. Due to the change in the WebKit, the access to asset
1868 * files through "file:///android_asset/" for the sub resources is more
1869 * restricted. If you provide null or empty string as baseUrl, you won't be
1870 * able to access asset files. If the baseUrl is anything other than
1871 * http(s)/ftp(s)/about/javascript as scheme, you can access asset files for
1872 * sub resources.
Cary Clarkd6982c92009-05-29 11:02:22 -04001873 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001874 * @param baseUrl Url to resolve relative paths with, if null defaults to
1875 * "about:blank"
1876 * @param data A String of data in the given encoding.
1877 * @param mimeType The MIMEType of the data. i.e. text/html. If null,
1878 * defaults to "text/html"
1879 * @param encoding The encoding of the data. i.e. utf-8, us-ascii
Leon Scroggins1bb1a912010-03-23 15:39:46 -04001880 * @param historyUrl URL to use as the history entry. Can be null.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001881 */
1882 public void loadDataWithBaseURL(String baseUrl, String data,
Leon Scroggins1bb1a912010-03-23 15:39:46 -04001883 String mimeType, String encoding, String historyUrl) {
Cary Clarkd6982c92009-05-29 11:02:22 -04001884
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001885 if (baseUrl != null && baseUrl.toLowerCase().startsWith("data:")) {
1886 loadData(data, mimeType, encoding);
1887 return;
1888 }
1889 switchOutDrawHistory();
Cary Clarkded054c2009-06-15 10:26:08 -04001890 WebViewCore.BaseUrlData arg = new WebViewCore.BaseUrlData();
1891 arg.mBaseUrl = baseUrl;
1892 arg.mData = data;
1893 arg.mMimeType = mimeType;
1894 arg.mEncoding = encoding;
Leon Scroggins1bb1a912010-03-23 15:39:46 -04001895 arg.mHistoryUrl = historyUrl;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001896 mWebViewCore.sendMessage(EventHub.LOAD_DATA, arg);
Leon Scroggins05919f22010-09-14 17:22:36 -04001897 clearHelpers();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001898 }
1899
1900 /**
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -07001901 * Saves the current view as a web archive.
1902 *
1903 * @param filename The filename where the archive should be placed.
1904 */
1905 public void saveWebArchive(String filename) {
1906 saveWebArchive(filename, false, null);
1907 }
1908
1909 /* package */ static class SaveWebArchiveMessage {
1910 SaveWebArchiveMessage (String basename, boolean autoname, ValueCallback<String> callback) {
1911 mBasename = basename;
1912 mAutoname = autoname;
1913 mCallback = callback;
1914 }
1915
1916 /* package */ final String mBasename;
1917 /* package */ final boolean mAutoname;
1918 /* package */ final ValueCallback<String> mCallback;
1919 /* package */ String mResultFile;
1920 }
1921
1922 /**
1923 * Saves the current view as a web archive.
1924 *
1925 * @param basename The filename where the archive should be placed.
1926 * @param autoname If false, takes basename to be a file. If true, basename
1927 * is assumed to be a directory in which a filename will be
1928 * chosen according to the url of the current page.
1929 * @param callback Called after the web archive has been saved. The
1930 * parameter for onReceiveValue will either be the filename
1931 * under which the file was saved, or null if saving the
1932 * file failed.
1933 */
1934 public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback) {
1935 mWebViewCore.sendMessage(EventHub.SAVE_WEBARCHIVE,
1936 new SaveWebArchiveMessage(basename, autoname, callback));
1937 }
1938
1939 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001940 * Stop the current load.
1941 */
1942 public void stopLoading() {
1943 // TODO: should we clear all the messages in the queue before sending
1944 // STOP_LOADING?
1945 switchOutDrawHistory();
1946 mWebViewCore.sendMessage(EventHub.STOP_LOADING);
1947 }
1948
1949 /**
1950 * Reload the current url.
1951 */
1952 public void reload() {
Leon Scroggins05919f22010-09-14 17:22:36 -04001953 clearHelpers();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001954 switchOutDrawHistory();
1955 mWebViewCore.sendMessage(EventHub.RELOAD);
1956 }
1957
1958 /**
1959 * Return true if this WebView has a back history item.
1960 * @return True iff this WebView has a back history item.
1961 */
1962 public boolean canGoBack() {
1963 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1964 synchronized (l) {
1965 if (l.getClearPending()) {
1966 return false;
1967 } else {
1968 return l.getCurrentIndex() > 0;
1969 }
1970 }
1971 }
1972
1973 /**
1974 * Go back in the history of this WebView.
1975 */
1976 public void goBack() {
1977 goBackOrForward(-1);
1978 }
1979
1980 /**
1981 * Return true if this WebView has a forward history item.
1982 * @return True iff this Webview has a forward history item.
1983 */
1984 public boolean canGoForward() {
1985 WebBackForwardList l = mCallbackProxy.getBackForwardList();
1986 synchronized (l) {
1987 if (l.getClearPending()) {
1988 return false;
1989 } else {
1990 return l.getCurrentIndex() < l.getSize() - 1;
1991 }
1992 }
1993 }
1994
1995 /**
1996 * Go forward in the history of this WebView.
1997 */
1998 public void goForward() {
1999 goBackOrForward(1);
2000 }
2001
2002 /**
2003 * Return true if the page can go back or forward the given
2004 * number of steps.
2005 * @param steps The negative or positive number of steps to move the
2006 * history.
2007 */
2008 public boolean canGoBackOrForward(int steps) {
2009 WebBackForwardList l = mCallbackProxy.getBackForwardList();
2010 synchronized (l) {
2011 if (l.getClearPending()) {
2012 return false;
2013 } else {
2014 int newIndex = l.getCurrentIndex() + steps;
2015 return newIndex >= 0 && newIndex < l.getSize();
2016 }
2017 }
2018 }
2019
2020 /**
2021 * Go to the history item that is the number of steps away from
2022 * the current item. Steps is negative if backward and positive
2023 * if forward.
2024 * @param steps The number of steps to take back or forward in the back
2025 * forward list.
2026 */
2027 public void goBackOrForward(int steps) {
2028 goBackOrForward(steps, false);
2029 }
2030
2031 private void goBackOrForward(int steps, boolean ignoreSnapshot) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002032 if (steps != 0) {
Leon Scroggins05919f22010-09-14 17:22:36 -04002033 clearHelpers();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002034 mWebViewCore.sendMessage(EventHub.GO_BACK_FORWARD, steps,
2035 ignoreSnapshot ? 1 : 0);
2036 }
2037 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002038
Elliott Slaughterf21d2e32010-07-14 18:08:54 -07002039 /**
2040 * Returns true if private browsing is enabled in this WebView.
2041 */
Elliott Slaughter7c2d1352010-08-20 15:57:18 -07002042 public boolean isPrivateBrowsingEnabled() {
Elliott Slaughterf21d2e32010-07-14 18:08:54 -07002043 return getSettings().isPrivateBrowsingEnabled();
2044 }
2045
Elliott Slaughter7c2d1352010-08-20 15:57:18 -07002046 private void startPrivateBrowsing() {
Elliott Slaughterf21d2e32010-07-14 18:08:54 -07002047 getSettings().setPrivateBrowsingEnabled(true);
Elliott Slaughterf21d2e32010-07-14 18:08:54 -07002048 }
2049
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002050 private boolean extendScroll(int y) {
2051 int finalY = mScroller.getFinalY();
2052 int newY = pinLocY(finalY + y);
2053 if (newY == finalY) return false;
2054 mScroller.setFinalY(newY);
2055 mScroller.extendDuration(computeDuration(0, y));
2056 return true;
2057 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002058
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002059 /**
2060 * Scroll the contents of the view up by half the view size
2061 * @param top true to jump to the top of the page
2062 * @return true if the page was scrolled
2063 */
2064 public boolean pageUp(boolean top) {
2065 if (mNativeClass == 0) {
2066 return false;
2067 }
Cary Clarke872f3a2009-06-11 09:51:11 -04002068 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002069 if (top) {
2070 // go to the top of the document
2071 return pinScrollTo(mScrollX, 0, true, 0);
2072 }
2073 // Page up
2074 int h = getHeight();
2075 int y;
2076 if (h > 2 * PAGE_SCROLL_OVERLAP) {
2077 y = -h + PAGE_SCROLL_OVERLAP;
2078 } else {
2079 y = -h / 2;
2080 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002081 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002082 : extendScroll(y);
2083 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002085 /**
2086 * Scroll the contents of the view down by half the page size
2087 * @param bottom true to jump to bottom of page
2088 * @return true if the page was scrolled
2089 */
2090 public boolean pageDown(boolean bottom) {
2091 if (mNativeClass == 0) {
2092 return false;
2093 }
Cary Clarke872f3a2009-06-11 09:51:11 -04002094 nativeClearCursor(); // start next trackball movement from page edge
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002095 if (bottom) {
Adam Powell637d3372010-08-25 14:37:03 -07002096 return pinScrollTo(mScrollX, computeRealVerticalScrollRange(), true, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002097 }
2098 // Page down.
2099 int h = getHeight();
2100 int y;
2101 if (h > 2 * PAGE_SCROLL_OVERLAP) {
2102 y = h - PAGE_SCROLL_OVERLAP;
2103 } else {
2104 y = h / 2;
2105 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002106 return mScroller.isFinished() ? pinScrollBy(0, y, true, 0)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002107 : extendScroll(y);
2108 }
2109
2110 /**
2111 * Clear the view so that onDraw() will draw nothing but white background,
2112 * and onMeasure() will return 0 if MeasureSpec is not MeasureSpec.EXACTLY
2113 */
2114 public void clearView() {
2115 mContentWidth = 0;
2116 mContentHeight = 0;
Shimeng (Simon) Wang464b6902011-03-16 11:27:44 -07002117 setBaseLayer(0, null, false, false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002118 mWebViewCore.sendMessage(EventHub.CLEAR_CONTENT);
2119 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002121 /**
2122 * Return a new picture that captures the current display of the webview.
2123 * This is a copy of the display, and will be unaffected if the webview
2124 * later loads a different URL.
2125 *
2126 * @return a picture containing the current contents of the view. Note this
2127 * picture is of the entire document, and is not restricted to the
2128 * bounds of the view.
2129 */
2130 public Picture capturePicture() {
Cary Clark5ffbb812010-10-08 15:38:39 -04002131 if (mNativeClass == 0) return null;
Grace Kloba8abd50b2010-07-08 15:02:14 -07002132 Picture result = new Picture();
2133 nativeCopyBaseContentToPicture(result);
2134 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002135 }
2136
2137 /**
2138 * Return true if the browser is displaying a TextView for text input.
2139 */
2140 private boolean inEditingMode() {
Leon Scroggins63284ed2010-04-09 14:54:46 -04002141 return mWebTextView != null && mWebTextView.getParent() != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002142 }
2143
Leon Scroggins6088e832010-02-17 13:17:32 -05002144 /**
2145 * Remove the WebTextView.
Leon Scroggins6088e832010-02-17 13:17:32 -05002146 */
Leon Scroggins2aed7762010-08-13 17:11:42 -04002147 private void clearTextEntry() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002148 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002149 mWebTextView.remove();
Leon Scroggins III6f992702010-08-12 14:24:00 -04002150 } else {
2151 // The keyboard may be open with the WebView as the served view
2152 hideSoftKeyboard();
2153 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002154 }
2155
Cary Clarkd6982c92009-05-29 11:02:22 -04002156 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002157 * Return the current scale of the WebView
2158 * @return The current scale.
2159 */
2160 public float getScale() {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002161 return mZoomManager.getScale();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002162 }
2163
2164 /**
2165 * Set the initial scale for the WebView. 0 means default. If
2166 * {@link WebSettings#getUseWideViewPort()} is true, it zooms out all the
2167 * way. Otherwise it starts with 100%. If initial scale is greater than 0,
2168 * WebView starts will this value as initial scale.
2169 *
2170 * @param scaleInPercent The initial scale in percent.
2171 */
2172 public void setInitialScale(int scaleInPercent) {
Derek Sollenberger341e22f2010-06-02 12:34:34 -04002173 mZoomManager.setInitialScaleInPercent(scaleInPercent);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002174 }
2175
2176 /**
2177 * Invoke the graphical zoom picker widget for this WebView. This will
2178 * result in the zoom widget appearing on the screen to control the zoom
2179 * level of this WebView.
2180 */
2181 public void invokeZoomPicker() {
2182 if (!getSettings().supportZoom()) {
2183 Log.w(LOGTAG, "This WebView doesn't support zoom.");
2184 return;
2185 }
Leon Scroggins05919f22010-09-14 17:22:36 -04002186 clearHelpers();
Derek Sollenberger90b6e482010-05-10 12:38:54 -04002187 mZoomManager.invokeZoomPicker();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002188 }
2189
2190 /**
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04002191 * Return a HitTestResult based on the current cursor node. If a HTML::a tag
Steve Block81f19ff2010-11-01 13:23:24 +00002192 * is found and the anchor has a non-JavaScript url, the HitTestResult type
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002193 * is set to SRC_ANCHOR_TYPE and the url is set in the "extra" field. If the
Steve Block81f19ff2010-11-01 13:23:24 +00002194 * anchor does not have a url or if it is a JavaScript url, the type will
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002195 * be UNKNOWN_TYPE and the url has to be retrieved through
2196 * {@link #requestFocusNodeHref} asynchronously. If a HTML::img tag is
2197 * found, the HitTestResult type is set to IMAGE_TYPE and the url is set in
2198 * the "extra" field. A type of
2199 * SRC_IMAGE_ANCHOR_TYPE indicates an anchor with a url that has an image as
2200 * a child node. If a phone number is found, the HitTestResult type is set
2201 * to PHONE_TYPE and the phone number is set in the "extra" field of
2202 * HitTestResult. If a map address is found, the HitTestResult type is set
2203 * to GEO_TYPE and the address is set in the "extra" field of HitTestResult.
2204 * If an email address is found, the HitTestResult type is set to EMAIL_TYPE
2205 * and the email is set in the "extra" field of HitTestResult. Otherwise,
2206 * HitTestResult type is set to UNKNOWN_TYPE.
2207 */
2208 public HitTestResult getHitTestResult() {
Cary Clarkb8491342010-11-29 16:23:19 -05002209 return hitTestResult(mInitialHitTestResult);
2210 }
2211
2212 private HitTestResult hitTestResult(HitTestResult fallback) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002213 if (mNativeClass == 0) {
2214 return null;
2215 }
2216
2217 HitTestResult result = new HitTestResult();
Cary Clarkd6982c92009-05-29 11:02:22 -04002218 if (nativeHasCursorNode()) {
2219 if (nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002220 result.setType(HitTestResult.EDIT_TEXT_TYPE);
Cary Clarkd6982c92009-05-29 11:02:22 -04002221 } else {
2222 String text = nativeCursorText();
2223 if (text != null) {
2224 if (text.startsWith(SCHEME_TEL)) {
2225 result.setType(HitTestResult.PHONE_TYPE);
2226 result.setExtra(text.substring(SCHEME_TEL.length()));
2227 } else if (text.startsWith(SCHEME_MAILTO)) {
2228 result.setType(HitTestResult.EMAIL_TYPE);
2229 result.setExtra(text.substring(SCHEME_MAILTO.length()));
2230 } else if (text.startsWith(SCHEME_GEO)) {
2231 result.setType(HitTestResult.GEO_TYPE);
2232 result.setExtra(URLDecoder.decode(text
2233 .substring(SCHEME_GEO.length())));
2234 } else if (nativeCursorIsAnchor()) {
2235 result.setType(HitTestResult.SRC_ANCHOR_TYPE);
2236 result.setExtra(text);
2237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002238 }
2239 }
Cary Clarkb8491342010-11-29 16:23:19 -05002240 } else if (fallback != null) {
2241 /* If webkit causes a rebuild while the long press is in progress,
2242 * the cursor node may be reset, even if it is still around. This
2243 * uses the cursor node saved when the touch began. Since the
2244 * nativeImageURI below only changes the result if it is successful,
2245 * this uses the data beneath the touch if available or the original
2246 * tap data otherwise.
2247 */
2248 Log.v(LOGTAG, "hitTestResult use fallback");
2249 result = fallback;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002250 }
2251 int type = result.getType();
2252 if (type == HitTestResult.UNKNOWN_TYPE
2253 || type == HitTestResult.SRC_ANCHOR_TYPE) {
2254 // Now check to see if it is an image.
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08002255 int contentX = viewToContentX(mLastTouchX + mScrollX);
2256 int contentY = viewToContentY(mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002257 String text = nativeImageURI(contentX, contentY);
2258 if (text != null) {
Cary Clarkd6982c92009-05-29 11:02:22 -04002259 result.setType(type == HitTestResult.UNKNOWN_TYPE ?
2260 HitTestResult.IMAGE_TYPE :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002261 HitTestResult.SRC_IMAGE_ANCHOR_TYPE);
2262 result.setExtra(text);
2263 }
2264 }
2265 return result;
2266 }
2267
Leon Scrogginse26efa32009-12-15 16:38:45 -05002268 // Called by JNI when the DOM has changed the focus. Clear the focus so
2269 // that new keys will go to the newly focused field
2270 private void domChangedFocus() {
2271 if (inEditingMode()) {
2272 mPrivateHandler.obtainMessage(DOM_FOCUS_CHANGED).sendToTarget();
2273 }
2274 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002275 /**
Cary Clark861368a2010-12-15 11:24:37 -05002276 * Request the anchor or image element URL at the last tapped point.
2277 * If hrefMsg is null, this method returns immediately and does not
2278 * dispatch hrefMsg to its target. If the tapped point hits an image,
2279 * an anchor, or an image in an anchor, the message associates
2280 * strings in named keys in its data. The value paired with the key
2281 * may be an empty string.
Cary Clarkd6982c92009-05-29 11:02:22 -04002282 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002283 * @param hrefMsg This message will be dispatched with the result of the
Cary Clark861368a2010-12-15 11:24:37 -05002284 * request. The message data contains three keys:
2285 * - "url" returns the anchor's href attribute.
2286 * - "title" returns the anchor's text.
2287 * - "src" returns the image's src attribute.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002288 */
2289 public void requestFocusNodeHref(Message hrefMsg) {
Cary Clarke41bb532010-11-30 15:59:59 -05002290 if (hrefMsg == null) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002291 return;
2292 }
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08002293 int contentX = viewToContentX(mLastTouchX + mScrollX);
2294 int contentY = viewToContentY(mLastTouchY + mScrollY);
Cary Clark3ecd7db2011-02-24 14:58:23 -05002295 if (nativeHasCursorNode()) {
2296 Rect cursorBounds = nativeGetCursorRingBounds();
2297 if (!cursorBounds.contains(contentX, contentY)) {
2298 int slop = viewToContentDimension(mNavSlop);
2299 cursorBounds.inset(-slop, -slop);
2300 if (cursorBounds.contains(contentX, contentY)) {
2301 contentX = (int) cursorBounds.centerX();
2302 contentY = (int) cursorBounds.centerY();
2303 }
2304 }
2305 }
Cary Clarke41bb532010-11-30 15:59:59 -05002306 mWebViewCore.sendMessage(EventHub.REQUEST_CURSOR_HREF,
2307 contentX, contentY, hrefMsg);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002308 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002309
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002310 /**
2311 * Request the url of the image last touched by the user. msg will be sent
2312 * to its target with a String representing the url as its object.
Cary Clarkd6982c92009-05-29 11:02:22 -04002313 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002314 * @param msg This message will be dispatched with the result of the request
2315 * as the data member with "url" as key. The result can be null.
2316 */
2317 public void requestImageRef(Message msg) {
Cary Clark7f970112009-10-15 15:29:08 -04002318 if (0 == mNativeClass) return; // client isn't initialized
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08002319 int contentX = viewToContentX(mLastTouchX + mScrollX);
2320 int contentY = viewToContentY(mLastTouchY + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002321 String ref = nativeImageURI(contentX, contentY);
2322 Bundle data = msg.getData();
2323 data.putString("url", ref);
2324 msg.setData(data);
2325 msg.sendToTarget();
2326 }
2327
Derek Sollenberger87b17be52010-06-01 11:49:31 -04002328 static int pinLoc(int x, int viewMax, int docMax) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002329// Log.d(LOGTAG, "-- pinLoc " + x + " " + viewMax + " " + docMax);
2330 if (docMax < viewMax) { // the doc has room on the sides for "blank"
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07002331 // pin the short document to the top/left of the screen
2332 x = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002333// Log.d(LOGTAG, "--- center " + x);
2334 } else if (x < 0) {
2335 x = 0;
2336// Log.d(LOGTAG, "--- zero");
2337 } else if (x + viewMax > docMax) {
2338 x = docMax - viewMax;
2339// Log.d(LOGTAG, "--- pin " + x);
2340 }
2341 return x;
2342 }
2343
2344 // Expects x in view coordinates
Derek Sollenberger03e48912010-05-18 17:03:42 -04002345 int pinLocX(int x) {
Adam Powell637d3372010-08-25 14:37:03 -07002346 if (mInOverScrollMode) return x;
2347 return pinLoc(x, getViewWidth(), computeRealHorizontalScrollRange());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002348 }
2349
2350 // Expects y in view coordinates
Derek Sollenberger03e48912010-05-18 17:03:42 -04002351 int pinLocY(int y) {
Adam Powell637d3372010-08-25 14:37:03 -07002352 if (mInOverScrollMode) return y;
Mike Reede5e63f42010-03-19 14:38:23 -04002353 return pinLoc(y, getViewHeightWithTitle(),
Adam Powell637d3372010-08-25 14:37:03 -07002354 computeRealVerticalScrollRange() + getTitleHeight());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002355 }
2356
Leon Scroggins0236e672009-09-02 21:12:08 -04002357 /**
2358 * A title bar which is embedded in this WebView, and scrolls along with it
2359 * vertically, but not horizontally.
2360 */
2361 private View mTitleBar;
2362
2363 /**
Michael Kolbc4ca53f2011-02-05 13:53:14 -08002364 * the title bar rendering gravity
2365 */
2366 private int mTitleGravity;
2367
2368 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04002369 * Add or remove a title bar to be embedded into the WebView, and scroll
2370 * along with it vertically, while remaining in view horizontally. Pass
2371 * null to remove the title bar from the WebView, and return to drawing
2372 * the WebView normally without translating to account for the title bar.
2373 * @hide
2374 */
Leon Scroggins078c52c2009-09-04 16:58:09 -04002375 public void setEmbeddedTitleBar(View v) {
2376 if (mTitleBar == v) return;
2377 if (mTitleBar != null) {
Leon Scroggins0236e672009-09-02 21:12:08 -04002378 removeView(mTitleBar);
Leon Scroggins078c52c2009-09-04 16:58:09 -04002379 }
2380 if (null != v) {
Leon Scroggins0236e672009-09-02 21:12:08 -04002381 addView(v, new AbsoluteLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -08002382 ViewGroup.LayoutParams.MATCH_PARENT,
Leon Scroggins078c52c2009-09-04 16:58:09 -04002383 ViewGroup.LayoutParams.WRAP_CONTENT, 0, 0));
Leon Scroggins0236e672009-09-02 21:12:08 -04002384 }
2385 mTitleBar = v;
2386 }
2387
2388 /**
Michael Kolbc4ca53f2011-02-05 13:53:14 -08002389 * Set where to render the embedded title bar
2390 * NO_GRAVITY at the top of the page
2391 * TOP at the top of the screen
2392 * @hide
2393 */
2394 public void setTitleBarGravity(int gravity) {
2395 mTitleGravity = gravity;
2396 }
2397
2398 /**
Leon Scrogginsd3997e52009-09-21 14:15:18 -04002399 * Given a distance in view space, convert it to content space. Note: this
2400 * does not reflect translation, just scaling, so this should not be called
2401 * with coordinates, but should be called for dimensions like width or
2402 * height.
2403 */
2404 private int viewToContentDimension(int d) {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002405 return Math.round(d * mZoomManager.getInvScale());
Leon Scrogginsd3997e52009-09-21 14:15:18 -04002406 }
2407
2408 /**
Leon Scroggins0236e672009-09-02 21:12:08 -04002409 * Given an x coordinate in view space, convert it to content space. Also
2410 * may be used for absolute heights (such as for the WebTextView's
2411 * textSize, which is unaffected by the height of the title bar).
2412 */
2413 /*package*/ int viewToContentX(int x) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04002414 return viewToContentDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002415 }
2416
Leon Scroggins0236e672009-09-02 21:12:08 -04002417 /**
2418 * Given a y coordinate in view space, convert it to content space.
2419 * Takes into account the height of the title bar if there is one
2420 * embedded into the WebView.
2421 */
2422 /*package*/ int viewToContentY(int y) {
Leon Scrogginsd3997e52009-09-21 14:15:18 -04002423 return viewToContentDimension(y - getTitleHeight());
Mike Reede8853fc2009-09-04 14:01:48 -04002424 }
2425
2426 /**
Nicolas Roard46318cf2010-05-10 15:15:51 -07002427 * Given a x coordinate in view space, convert it to content space.
2428 * Returns the result as a float.
2429 */
2430 private float viewToContentXf(int x) {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002431 return x * mZoomManager.getInvScale();
Nicolas Roard46318cf2010-05-10 15:15:51 -07002432 }
2433
2434 /**
2435 * Given a y coordinate in view space, convert it to content space.
2436 * Takes into account the height of the title bar if there is one
2437 * embedded into the WebView. Returns the result as a float.
2438 */
2439 private float viewToContentYf(int y) {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002440 return (y - getTitleHeight()) * mZoomManager.getInvScale();
Nicolas Roard46318cf2010-05-10 15:15:51 -07002441 }
2442
2443 /**
Mike Reede8853fc2009-09-04 14:01:48 -04002444 * Given a distance in content space, convert it to view space. Note: this
2445 * does not reflect translation, just scaling, so this should not be called
2446 * with coordinates, but should be called for dimensions like width or
2447 * height.
2448 */
2449 /*package*/ int contentToViewDimension(int d) {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002450 return Math.round(d * mZoomManager.getScale());
Leon Scroggins0236e672009-09-02 21:12:08 -04002451 }
2452
2453 /**
2454 * Given an x coordinate in content space, convert it to view
Leon Scrogginsd3997e52009-09-21 14:15:18 -04002455 * space.
Leon Scroggins0236e672009-09-02 21:12:08 -04002456 */
2457 /*package*/ int contentToViewX(int x) {
Mike Reede8853fc2009-09-04 14:01:48 -04002458 return contentToViewDimension(x);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002459 }
2460
Leon Scroggins0236e672009-09-02 21:12:08 -04002461 /**
2462 * Given a y coordinate in content space, convert it to view
2463 * space. Takes into account the height of the title bar.
2464 */
2465 /*package*/ int contentToViewY(int y) {
Mike Reede8853fc2009-09-04 14:01:48 -04002466 return contentToViewDimension(y) + getTitleHeight();
Leon Scroggins0236e672009-09-02 21:12:08 -04002467 }
2468
Mike Reede9e86b82009-09-15 11:26:53 -04002469 private Rect contentToViewRect(Rect x) {
2470 return new Rect(contentToViewX(x.left), contentToViewY(x.top),
2471 contentToViewX(x.right), contentToViewY(x.bottom));
2472 }
2473
2474 /* To invalidate a rectangle in content coordinates, we need to transform
2475 the rect into view coordinates, so we can then call invalidate(...).
2476
2477 Normally, we would just call contentToView[XY](...), which eventually
2478 calls Math.round(coordinate * mActualScale). However, for invalidates,
2479 we need to account for the slop that occurs with antialiasing. To
2480 address that, we are a little more liberal in the size of the rect that
2481 we invalidate.
2482
2483 This liberal calculation calls floor() for the top/left, and ceil() for
2484 the bottom/right coordinates. This catches the possible extra pixels of
2485 antialiasing that we might have missed with just round().
2486 */
2487
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002488 // Called by JNI to invalidate the View, given rectangle coordinates in
2489 // content space
2490 private void viewInvalidate(int l, int t, int r, int b) {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002491 final float scale = mZoomManager.getScale();
Mike Reede9e86b82009-09-15 11:26:53 -04002492 final int dy = getTitleHeight();
2493 invalidate((int)Math.floor(l * scale),
2494 (int)Math.floor(t * scale) + dy,
2495 (int)Math.ceil(r * scale),
2496 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002497 }
2498
2499 // Called by JNI to invalidate the View after a delay, given rectangle
2500 // coordinates in content space
2501 private void viewInvalidateDelayed(long delay, int l, int t, int r, int b) {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002502 final float scale = mZoomManager.getScale();
Mike Reede9e86b82009-09-15 11:26:53 -04002503 final int dy = getTitleHeight();
2504 postInvalidateDelayed(delay,
2505 (int)Math.floor(l * scale),
2506 (int)Math.floor(t * scale) + dy,
2507 (int)Math.ceil(r * scale),
2508 (int)Math.ceil(b * scale) + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002509 }
2510
Mike Reede9e86b82009-09-15 11:26:53 -04002511 private void invalidateContentRect(Rect r) {
2512 viewInvalidate(r.left, r.top, r.right, r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002513 }
2514
Cary Clark278ce052009-08-31 16:08:42 -04002515 // stop the scroll animation, and don't let a subsequent fling add
2516 // to the existing velocity
2517 private void abortAnimation() {
2518 mScroller.abortAnimation();
2519 mLastVelocity = 0;
2520 }
2521
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002522 /* call from webcoreview.draw(), so we're still executing in the UI thread
2523 */
2524 private void recordNewContentSize(int w, int h, boolean updateLayout) {
2525
2526 // premature data from webkit, ignore
2527 if ((w | h) == 0) {
2528 return;
2529 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002530
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002531 // don't abort a scroll animation if we didn't change anything
2532 if (mContentWidth != w || mContentHeight != h) {
2533 // record new dimensions
2534 mContentWidth = w;
2535 mContentHeight = h;
2536 // If history Picture is drawn, don't update scroll. They will be
2537 // updated when we get out of that mode.
2538 if (!mDrawHistory) {
2539 // repin our scroll, taking into account the new content size
Derek Sollenberger03e48912010-05-18 17:03:42 -04002540 updateScrollCoordinates(pinLocX(mScrollX), pinLocY(mScrollY));
Leon Scrogginsd84e7d52009-09-29 11:11:45 -04002541 if (!mScroller.isFinished()) {
2542 // We are in the middle of a scroll. Repin the final scroll
2543 // position.
2544 mScroller.setFinalX(pinLocX(mScroller.getFinalX()));
2545 mScroller.setFinalY(pinLocY(mScroller.getFinalY()));
2546 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002547 }
2548 }
2549 contentSizeChanged(updateLayout);
2550 }
2551
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002552 // Used to avoid sending many visible rect messages.
2553 private Rect mLastVisibleRectSent;
2554 private Rect mLastGlobalRect;
2555
Derek Sollenberger03e48912010-05-18 17:03:42 -04002556 Rect sendOurVisibleRect() {
Derek Sollenberger293c3602010-06-04 10:44:48 -04002557 if (mZoomManager.isPreventingWebkitUpdates()) return mLastVisibleRectSent;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002558 Rect rect = new Rect();
2559 calcOurContentVisibleRect(rect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002560 // Rect.equals() checks for null input.
2561 if (!rect.equals(mLastVisibleRectSent)) {
Cary Clarked56eda2009-06-18 09:48:47 -04002562 Point pos = new Point(rect.left, rect.top);
John Reck636ce002011-01-30 16:23:09 -08002563 mWebViewCore.removeMessages(EventHub.SET_SCROLL_OFFSET);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002564 mWebViewCore.sendMessage(EventHub.SET_SCROLL_OFFSET,
Patrick Scottfa8be1c2011-02-02 14:09:34 -05002565 nativeMoveGeneration(), mSendScrollEvent ? 1 : 0, pos);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002566 mLastVisibleRectSent = rect;
Cary Clark32820242010-12-03 10:27:20 -05002567 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002568 }
2569 Rect globalRect = new Rect();
2570 if (getGlobalVisibleRect(globalRect)
2571 && !globalRect.equals(mLastGlobalRect)) {
Cary Clark3524be92009-06-22 13:09:11 -04002572 if (DebugFlags.WEB_VIEW) {
2573 Log.v(LOGTAG, "sendOurVisibleRect=(" + globalRect.left + ","
2574 + globalRect.top + ",r=" + globalRect.right + ",b="
2575 + globalRect.bottom);
2576 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002577 // TODO: the global offset is only used by windowRect()
2578 // in ChromeClientAndroid ; other clients such as touch
2579 // and mouse events could return view + screen relative points.
2580 mWebViewCore.sendMessage(EventHub.SET_GLOBAL_BOUNDS, globalRect);
2581 mLastGlobalRect = globalRect;
2582 }
2583 return rect;
2584 }
2585
2586 // Sets r to be the visible rectangle of our webview in view coordinates
2587 private void calcOurVisibleRect(Rect r) {
2588 Point p = new Point();
2589 getGlobalVisibleRect(r, p);
2590 r.offset(-p.x, -p.y);
2591 }
2592
2593 // Sets r to be our visible rectangle in content coordinates
2594 private void calcOurContentVisibleRect(Rect r) {
2595 calcOurVisibleRect(r);
Teng-Hui Zhuc99821d2010-12-02 15:44:29 -08002596 r.left = viewToContentX(r.left);
Leon Scroggins37df6a82009-09-23 10:31:23 -04002597 // viewToContentY will remove the total height of the title bar. Add
2598 // the visible height back in to account for the fact that if the title
2599 // bar is partially visible, the part of the visible rect which is
2600 // displaying our content is displaced by that amount.
Teng-Hui Zhuc99821d2010-12-02 15:44:29 -08002601 r.top = viewToContentY(r.top + getVisibleTitleHeight());
2602 r.right = viewToContentX(r.right);
2603 r.bottom = viewToContentY(r.bottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002604 }
2605
Nicolas Roard46318cf2010-05-10 15:15:51 -07002606 // Sets r to be our visible rectangle in content coordinates. We use this
2607 // method on the native side to compute the position of the fixed layers.
2608 // Uses floating coordinates (necessary to correctly place elements when
2609 // the scale factor is not 1)
2610 private void calcOurContentVisibleRectF(RectF r) {
2611 Rect ri = new Rect(0,0,0,0);
2612 calcOurVisibleRect(ri);
Teng-Hui Zhuc99821d2010-12-02 15:44:29 -08002613 r.left = viewToContentXf(ri.left);
Nicolas Roard46318cf2010-05-10 15:15:51 -07002614 // viewToContentY will remove the total height of the title bar. Add
2615 // the visible height back in to account for the fact that if the title
2616 // bar is partially visible, the part of the visible rect which is
2617 // displaying our content is displaced by that amount.
Teng-Hui Zhuc99821d2010-12-02 15:44:29 -08002618 r.top = viewToContentYf(ri.top + getVisibleTitleHeight());
2619 r.right = viewToContentXf(ri.right);
2620 r.bottom = viewToContentYf(ri.bottom);
Nicolas Roard46318cf2010-05-10 15:15:51 -07002621 }
2622
Grace Klobaef347ef2009-07-30 11:20:32 -07002623 static class ViewSizeData {
2624 int mWidth;
2625 int mHeight;
Shimeng (Simon) Wanga0cc8642011-02-03 10:24:03 -08002626 float mHeightWidthRatio;
Shimeng (Simon) Wang48fc9092011-02-03 14:29:54 -08002627 int mActualViewHeight;
Grace Klobaef347ef2009-07-30 11:20:32 -07002628 int mTextWrapWidth;
Grace Kloba3a0def22010-01-23 21:11:54 -08002629 int mAnchorX;
2630 int mAnchorY;
Grace Klobaef347ef2009-07-30 11:20:32 -07002631 float mScale;
Cary Clark6d45acc2009-08-27 15:42:57 -04002632 boolean mIgnoreHeight;
Grace Klobaef347ef2009-07-30 11:20:32 -07002633 }
2634
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002635 /**
2636 * Compute unzoomed width and height, and if they differ from the last
Derek Sollenberger03e48912010-05-18 17:03:42 -04002637 * values we sent, send them to webkit (to be used as new viewport)
2638 *
2639 * @param force ensures that the message is sent to webkit even if the width
2640 * or height has not changed since the last message
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002641 *
2642 * @return true if new values were sent
2643 */
Derek Sollenberger03e48912010-05-18 17:03:42 -04002644 boolean sendViewSizeZoom(boolean force) {
Derek Sollenberger293c3602010-06-04 10:44:48 -04002645 if (mZoomManager.isPreventingWebkitUpdates()) return false;
Grace Kloba3a0def22010-01-23 21:11:54 -08002646
Grace Klobaef347ef2009-07-30 11:20:32 -07002647 int viewWidth = getViewWidth();
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002648 int newWidth = Math.round(viewWidth * mZoomManager.getInvScale());
Shimeng (Simon) Wang48fc9092011-02-03 14:29:54 -08002649 // This height could be fixed and be different from actual visible height.
Shimeng (Simon) Wanga0cc8642011-02-03 10:24:03 -08002650 int viewHeight = getViewHeightWithTitle() - getTitleHeight();
2651 int newHeight = Math.round(viewHeight * mZoomManager.getInvScale());
2652 // Make the ratio more accurate than (newHeight / newWidth), since the
2653 // latter both are calculated and rounded.
2654 float heightWidthRatio = (float) viewHeight / viewWidth;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002655 /*
2656 * Because the native side may have already done a layout before the
2657 * View system was able to measure us, we have to send a height of 0 to
2658 * remove excess whitespace when we grow our width. This will trigger a
2659 * layout and a change in content size. This content size change will
2660 * mean that contentSizeChanged will either call this method directly or
2661 * indirectly from onSizeChanged.
2662 */
2663 if (newWidth > mLastWidthSent && mWrapContent) {
2664 newHeight = 0;
Shimeng (Simon) Wanga0cc8642011-02-03 10:24:03 -08002665 heightWidthRatio = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002666 }
Shimeng (Simon) Wang6a0b5ca2011-02-08 09:49:35 -08002667 // Actual visible content height.
2668 int actualViewHeight = Math.round(getViewHeight() * mZoomManager.getInvScale());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002669 // Avoid sending another message if the dimensions have not changed.
Shimeng (Simon) Wang48fc9092011-02-03 14:29:54 -08002670 if (newWidth != mLastWidthSent || newHeight != mLastHeightSent || force ||
2671 actualViewHeight != mLastActualHeightSent) {
Grace Klobaef347ef2009-07-30 11:20:32 -07002672 ViewSizeData data = new ViewSizeData();
2673 data.mWidth = newWidth;
2674 data.mHeight = newHeight;
Shimeng (Simon) Wanga0cc8642011-02-03 10:24:03 -08002675 data.mHeightWidthRatio = heightWidthRatio;
Shimeng (Simon) Wang48fc9092011-02-03 14:29:54 -08002676 data.mActualViewHeight = actualViewHeight;
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002677 data.mTextWrapWidth = Math.round(viewWidth / mZoomManager.getTextWrapScale());
2678 data.mScale = mZoomManager.getScale();
Derek Sollenberger293c3602010-06-04 10:44:48 -04002679 data.mIgnoreHeight = mZoomManager.isFixedLengthAnimationInProgress()
2680 && !mHeightCanMeasure;
Derek Sollenberger15c5ddb2010-06-10 12:31:29 -04002681 data.mAnchorX = mZoomManager.getDocumentAnchorX();
2682 data.mAnchorY = mZoomManager.getDocumentAnchorY();
Grace Klobaef347ef2009-07-30 11:20:32 -07002683 mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002684 mLastWidthSent = newWidth;
2685 mLastHeightSent = newHeight;
Shimeng (Simon) Wang48fc9092011-02-03 14:29:54 -08002686 mLastActualHeightSent = actualViewHeight;
Derek Sollenberger15c5ddb2010-06-10 12:31:29 -04002687 mZoomManager.clearDocumentAnchor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002688 return true;
2689 }
2690 return false;
2691 }
2692
Adam Powell637d3372010-08-25 14:37:03 -07002693 private int computeRealHorizontalScrollRange() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002694 if (mDrawHistory) {
2695 return mHistoryWidth;
2696 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002697 // to avoid rounding error caused unnecessary scrollbar, use floor
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002698 return (int) Math.floor(mContentWidth * mZoomManager.getScale());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002699 }
2700 }
2701
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002702 @Override
Adam Powell637d3372010-08-25 14:37:03 -07002703 protected int computeHorizontalScrollRange() {
2704 int range = computeRealHorizontalScrollRange();
2705
2706 // Adjust reported range if overscrolled to compress the scroll bars
2707 final int scrollX = mScrollX;
2708 final int overscrollRight = computeMaxScrollX();
2709 if (scrollX < 0) {
2710 range -= scrollX;
2711 } else if (scrollX > overscrollRight) {
2712 range += scrollX - overscrollRight;
2713 }
2714
2715 return range;
2716 }
2717
2718 @Override
2719 protected int computeHorizontalScrollOffset() {
2720 return Math.max(mScrollX, 0);
2721 }
2722
2723 private int computeRealVerticalScrollRange() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002724 if (mDrawHistory) {
2725 return mHistoryHeight;
2726 } else {
Grace Klobae621d6f2009-09-11 13:20:39 -07002727 // to avoid rounding error caused unnecessary scrollbar, use floor
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04002728 return (int) Math.floor(mContentHeight * mZoomManager.getScale());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002729 }
2730 }
2731
Leon Scroggins0236e672009-09-02 21:12:08 -04002732 @Override
Adam Powell637d3372010-08-25 14:37:03 -07002733 protected int computeVerticalScrollRange() {
2734 int range = computeRealVerticalScrollRange();
2735
2736 // Adjust reported range if overscrolled to compress the scroll bars
2737 final int scrollY = mScrollY;
2738 final int overscrollBottom = computeMaxScrollY();
2739 if (scrollY < 0) {
2740 range -= scrollY;
2741 } else if (scrollY > overscrollBottom) {
2742 range += scrollY - overscrollBottom;
2743 }
2744
2745 return range;
2746 }
2747
2748 @Override
Leon Scroggins0236e672009-09-02 21:12:08 -04002749 protected int computeVerticalScrollOffset() {
Mike Reede8853fc2009-09-04 14:01:48 -04002750 return Math.max(mScrollY - getTitleHeight(), 0);
Leon Scroggins0236e672009-09-02 21:12:08 -04002751 }
2752
2753 @Override
2754 protected int computeVerticalScrollExtent() {
2755 return getViewHeight();
2756 }
2757
Mike Reede8853fc2009-09-04 14:01:48 -04002758 /** @hide */
2759 @Override
2760 protected void onDrawVerticalScrollBar(Canvas canvas,
2761 Drawable scrollBar,
2762 int l, int t, int r, int b) {
Adam Powell637d3372010-08-25 14:37:03 -07002763 if (mScrollY < 0) {
2764 t -= mScrollY;
2765 }
Mike Reede8853fc2009-09-04 14:01:48 -04002766 scrollBar.setBounds(l, t + getVisibleTitleHeight(), r, b);
2767 scrollBar.draw(canvas);
2768 }
2769
Adam Powell637d3372010-08-25 14:37:03 -07002770 @Override
2771 protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX,
2772 boolean clampedY) {
Patrick Scott62310912010-12-06 17:44:50 -05002773 // Special-case layer scrolling so that we do not trigger normal scroll
2774 // updating.
2775 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
2776 nativeScrollLayer(mScrollingLayer, scrollX, scrollY);
2777 mScrollingLayerRect.left = scrollX;
2778 mScrollingLayerRect.top = scrollY;
2779 invalidate();
2780 return;
2781 }
Adam Powell637d3372010-08-25 14:37:03 -07002782 mInOverScrollMode = false;
2783 int maxX = computeMaxScrollX();
2784 int maxY = computeMaxScrollY();
2785 if (maxX == 0) {
2786 // do not over scroll x if the page just fits the screen
2787 scrollX = pinLocX(scrollX);
2788 } else if (scrollX < 0 || scrollX > maxX) {
2789 mInOverScrollMode = true;
2790 }
2791 if (scrollY < 0 || scrollY > maxY) {
2792 mInOverScrollMode = true;
2793 }
2794
2795 int oldX = mScrollX;
2796 int oldY = mScrollY;
2797
2798 super.scrollTo(scrollX, scrollY);
2799
2800 if (mOverScrollGlow != null) {
2801 mOverScrollGlow.pullGlow(mScrollX, mScrollY, oldX, oldY, maxX, maxY);
2802 }
2803 }
2804
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002805 /**
2806 * Get the url for the current page. This is not always the same as the url
2807 * passed to WebViewClient.onPageStarted because although the load for
2808 * that url has begun, the current page may not have changed.
2809 * @return The url for the current page.
2810 */
2811 public String getUrl() {
2812 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2813 return h != null ? h.getUrl() : null;
2814 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002815
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002816 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04002817 * Get the original url for the current page. This is not always the same
2818 * as the url passed to WebViewClient.onPageStarted because although the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002819 * load for that url has begun, the current page may not have changed.
2820 * Also, there may have been redirects resulting in a different url to that
2821 * originally requested.
2822 * @return The url that was originally requested for the current page.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002823 */
2824 public String getOriginalUrl() {
2825 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2826 return h != null ? h.getOriginalUrl() : null;
2827 }
2828
2829 /**
2830 * Get the title for the current page. This is the title of the current page
2831 * until WebViewClient.onReceivedTitle is called.
2832 * @return The title for the current page.
2833 */
2834 public String getTitle() {
2835 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2836 return h != null ? h.getTitle() : null;
2837 }
2838
2839 /**
2840 * Get the favicon for the current page. This is the favicon of the current
2841 * page until WebViewClient.onReceivedIcon is called.
2842 * @return The favicon for the current page.
2843 */
2844 public Bitmap getFavicon() {
2845 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2846 return h != null ? h.getFavicon() : null;
2847 }
2848
2849 /**
Ben Murdoch372dfc82010-07-01 15:56:01 +01002850 * Get the touch icon url for the apple-touch-icon <link> element, or
2851 * a URL on this site's server pointing to the standard location of a
2852 * touch icon.
Patrick Scott2ba12622009-08-04 13:20:05 -04002853 * @hide
2854 */
2855 public String getTouchIconUrl() {
2856 WebHistoryItem h = mCallbackProxy.getBackForwardList().getCurrentItem();
2857 return h != null ? h.getTouchIconUrl() : null;
2858 }
2859
2860 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002861 * Get the progress for the current page.
2862 * @return The progress for the current page between 0 and 100.
2863 */
2864 public int getProgress() {
2865 return mCallbackProxy.getProgress();
2866 }
Cary Clarkd6982c92009-05-29 11:02:22 -04002867
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002868 /**
2869 * @return the height of the HTML content.
2870 */
2871 public int getContentHeight() {
2872 return mContentHeight;
2873 }
2874
2875 /**
Leon Scrogginsea96d1e2009-09-23 13:41:01 -04002876 * @return the width of the HTML content.
2877 * @hide
2878 */
2879 public int getContentWidth() {
2880 return mContentWidth;
2881 }
2882
2883 /**
Steve Block81f19ff2010-11-01 13:23:24 +00002884 * Pause all layout, parsing, and JavaScript timers for all webviews. This
Mike Reedd205d5b2009-05-27 11:02:29 -04002885 * is a global requests, not restricted to just this webview. This can be
2886 * useful if the application has been paused.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002887 */
2888 public void pauseTimers() {
2889 mWebViewCore.sendMessage(EventHub.PAUSE_TIMERS);
2890 }
2891
2892 /**
Steve Block81f19ff2010-11-01 13:23:24 +00002893 * Resume all layout, parsing, and JavaScript timers for all webviews.
Mike Reedd205d5b2009-05-27 11:02:29 -04002894 * This will resume dispatching all timers.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002895 */
2896 public void resumeTimers() {
2897 mWebViewCore.sendMessage(EventHub.RESUME_TIMERS);
2898 }
2899
2900 /**
Steve Block81f19ff2010-11-01 13:23:24 +00002901 * Call this to pause any extra processing associated with this WebView and
2902 * its associated DOM, plugins, JavaScript etc. For example, if the WebView
2903 * is taken offscreen, this could be called to reduce unnecessary CPU or
2904 * network traffic. When the WebView is again "active", call onResume().
Mike Reedd205d5b2009-05-27 11:02:29 -04002905 *
Steve Block81f19ff2010-11-01 13:23:24 +00002906 * Note that this differs from pauseTimers(), which affects all WebViews.
Mike Reedd205d5b2009-05-27 11:02:29 -04002907 */
2908 public void onPause() {
2909 if (!mIsPaused) {
2910 mIsPaused = true;
2911 mWebViewCore.sendMessage(EventHub.ON_PAUSE);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -08002912 // We want to pause the current playing video when switching out
2913 // from the current WebView/tab.
2914 if (mHTML5VideoViewProxy != null) {
2915 mHTML5VideoViewProxy.pauseAndDispatch();
2916 }
Mike Reedd205d5b2009-05-27 11:02:29 -04002917 }
2918 }
2919
2920 /**
Steve Block81f19ff2010-11-01 13:23:24 +00002921 * Call this to resume a WebView after a previous call to onPause().
Mike Reedd205d5b2009-05-27 11:02:29 -04002922 */
2923 public void onResume() {
2924 if (mIsPaused) {
2925 mIsPaused = false;
2926 mWebViewCore.sendMessage(EventHub.ON_RESUME);
2927 }
2928 }
2929
2930 /**
2931 * Returns true if the view is paused, meaning onPause() was called. Calling
2932 * onResume() sets the paused state back to false.
2933 * @hide
2934 */
2935 public boolean isPaused() {
2936 return mIsPaused;
2937 }
2938
2939 /**
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002940 * Call this to inform the view that memory is low so that it can
2941 * free any available memory.
Derek Sollenbergere0155e92009-06-10 15:35:45 -04002942 */
2943 public void freeMemory() {
2944 mWebViewCore.sendMessage(EventHub.FREE_MEMORY);
2945 }
2946
2947 /**
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002948 * Clear the resource cache. Note that the cache is per-application, so
2949 * this will clear the cache for all WebViews used.
2950 *
2951 * @param includeDiskFiles If false, only the RAM cache is cleared.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002952 */
2953 public void clearCache(boolean includeDiskFiles) {
Mike Hearnadcd2ed2009-01-21 16:44:36 +01002954 // Note: this really needs to be a static method as it clears cache for all
2955 // WebView. But we need mWebViewCore to send message to WebCore thread, so
2956 // we can't make this static.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002957 mWebViewCore.sendMessage(EventHub.CLEAR_CACHE,
2958 includeDiskFiles ? 1 : 0, 0);
2959 }
2960
2961 /**
2962 * Make sure that clearing the form data removes the adapter from the
2963 * currently focused textfield if there is one.
2964 */
2965 public void clearFormData() {
2966 if (inEditingMode()) {
2967 AutoCompleteAdapter adapter = null;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04002968 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08002969 }
2970 }
2971
2972 /**
2973 * Tell the WebView to clear its internal back/forward list.
2974 */
2975 public void clearHistory() {
2976 mCallbackProxy.getBackForwardList().setClearPending();
2977 mWebViewCore.sendMessage(EventHub.CLEAR_HISTORY);
2978 }
2979
2980 /**
2981 * Clear the SSL preferences table stored in response to proceeding with SSL
2982 * certificate errors.
2983 */
2984 public void clearSslPreferences() {
2985 mWebViewCore.sendMessage(EventHub.CLEAR_SSL_PREF_TABLE);
2986 }
2987
2988 /**
2989 * Return the WebBackForwardList for this WebView. This contains the
2990 * back/forward list for use in querying each item in the history stack.
2991 * This is a copy of the private WebBackForwardList so it contains only a
2992 * snapshot of the current state. Multiple calls to this method may return
2993 * different objects. The object returned from this method will not be
2994 * updated to reflect any new state.
2995 */
2996 public WebBackForwardList copyBackForwardList() {
2997 return mCallbackProxy.getBackForwardList().clone();
2998 }
2999
3000 /*
3001 * Highlight and scroll to the next occurance of String in findAll.
Cary Clarkd6982c92009-05-29 11:02:22 -04003002 * Wraps the page infinitely, and scrolls. Must be called after
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003003 * calling findAll.
3004 *
3005 * @param forward Direction to search.
3006 */
3007 public void findNext(boolean forward) {
Cary Clark7f970112009-10-15 15:29:08 -04003008 if (0 == mNativeClass) return; // client isn't initialized
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003009 nativeFindNext(forward);
3010 }
3011
3012 /*
3013 * Find all instances of find on the page and highlight them.
3014 * @param find String to find.
3015 * @return int The number of occurances of the String "find"
3016 * that were found.
3017 */
3018 public int findAll(String find) {
Cary Clark7f970112009-10-15 15:29:08 -04003019 if (0 == mNativeClass) return 0; // client isn't initialized
Cary Clarkde023c12010-03-03 10:05:16 -05003020 int result = find != null ? nativeFindAll(find.toLowerCase(),
Leon Scroggins4f4a5672010-10-19 13:58:11 -04003021 find.toUpperCase(), find.equalsIgnoreCase(mLastFind)) : 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003022 invalidate();
Leon Scroggins5de63892009-10-29 09:48:43 -04003023 mLastFind = find;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003024 return result;
3025 }
3026
Cary Clark3403eb32010-03-03 10:05:16 -05003027 /**
Leon Scroggins93553d72011-03-08 11:43:40 -05003028 * Start an ActionMode for finding text in this WebView. Only works if this
3029 * WebView is attached to the view system.
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003030 * @param text If non-null, will be the initial text to search for.
3031 * Otherwise, the last String searched for in this WebView will
3032 * be used to start.
Leon Scroggins571354f2011-01-04 10:12:41 -05003033 * @param showIme If true, show the IME, assuming the user will begin typing.
3034 * If false and text is non-null, perform a find all.
3035 * @return boolean True if the find dialog is shown, false otherwise.
Cary Clarkde023c12010-03-03 10:05:16 -05003036 */
Leon Scroggins571354f2011-01-04 10:12:41 -05003037 public boolean showFindDialog(String text, boolean showIme) {
Leon Scroggins19abf2d2011-01-10 14:50:04 -05003038 FindActionModeCallback callback = new FindActionModeCallback(mContext);
Leon Scroggins93553d72011-03-08 11:43:40 -05003039 if (getParent() == null || startActionMode(callback) == null) {
Leon Scroggins571354f2011-01-04 10:12:41 -05003040 // Could not start the action mode, so end Find on page
Leon Scroggins571354f2011-01-04 10:12:41 -05003041 return false;
3042 }
Leon Scroggins19abf2d2011-01-10 14:50:04 -05003043 mFindCallback = callback;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003044 setFindIsUp(true);
3045 mFindCallback.setWebView(this);
Leon Scroggins571354f2011-01-04 10:12:41 -05003046 if (showIme) {
3047 mFindCallback.showSoftInput();
3048 } else if (text != null) {
3049 mFindCallback.setText(text);
3050 mFindCallback.findAll();
3051 return true;
3052 }
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003053 if (text == null) {
3054 text = mLastFind;
3055 }
3056 if (text != null) {
3057 mFindCallback.setText(text);
3058 }
Leon Scroggins571354f2011-01-04 10:12:41 -05003059 return true;
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003060 }
3061
3062 /**
3063 * Keep track of the find callback so that we can remove its titlebar if
3064 * necessary.
3065 */
3066 private FindActionModeCallback mFindCallback;
3067
3068 /**
3069 * Toggle whether the find dialog is showing, for both native and Java.
3070 */
3071 private void setFindIsUp(boolean isUp) {
Cary Clarkde023c12010-03-03 10:05:16 -05003072 mFindIsUp = isUp;
Cary Clarkde023c12010-03-03 10:05:16 -05003073 if (0 == mNativeClass) return; // client isn't initialized
3074 nativeSetFindIsUp(isUp);
3075 }
3076
Leon Scroggins III26723fc2010-04-19 13:21:42 -04003077 /**
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003078 * Return the index of the currently highlighted match.
Leon Scroggins III26723fc2010-04-19 13:21:42 -04003079 */
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003080 int findIndex() {
Leon Scroggins6a367f52010-05-06 17:37:40 -04003081 if (0 == mNativeClass) return -1;
3082 return nativeFindIndex();
3083 }
3084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003085 // Used to know whether the find dialog is open. Affects whether
3086 // or not we draw the highlights for matches.
3087 private boolean mFindIsUp;
Cary Clarkde023c12010-03-03 10:05:16 -05003088
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003089 // Keep track of the last string sent, so we can search again when find is
3090 // reopened.
Leon Scroggins5de63892009-10-29 09:48:43 -04003091 private String mLastFind;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003092
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003093 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04003094 * Return the first substring consisting of the address of a physical
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003095 * location. Currently, only addresses in the United States are detected,
3096 * and consist of:
3097 * - a house number
3098 * - a street name
3099 * - a street type (Road, Circle, etc), either spelled out or abbreviated
3100 * - a city name
3101 * - a state or territory, either spelled out or two-letter abbr.
3102 * - an optional 5 digit or 9 digit zip code.
3103 *
3104 * All names must be correctly capitalized, and the zip code, if present,
3105 * must be valid for the state. The street type must be a standard USPS
3106 * spelling or abbreviation. The state or territory must also be spelled
Cary Clarkd6982c92009-05-29 11:02:22 -04003107 * or abbreviated using USPS standards. The house number may not exceed
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003108 * five digits.
3109 * @param addr The string to search for addresses.
3110 *
3111 * @return the address, or if no address is found, return null.
3112 */
3113 public static String findAddress(String addr) {
Cary Clark3e399de2009-06-17 10:00:57 -04003114 return findAddress(addr, false);
3115 }
3116
3117 /**
3118 * @hide
3119 * Return the first substring consisting of the address of a physical
3120 * location. Currently, only addresses in the United States are detected,
3121 * and consist of:
3122 * - a house number
3123 * - a street name
3124 * - a street type (Road, Circle, etc), either spelled out or abbreviated
3125 * - a city name
3126 * - a state or territory, either spelled out or two-letter abbr.
3127 * - an optional 5 digit or 9 digit zip code.
3128 *
3129 * Names are optionally capitalized, and the zip code, if present,
3130 * must be valid for the state. The street type must be a standard USPS
3131 * spelling or abbreviation. The state or territory must also be spelled
3132 * or abbreviated using USPS standards. The house number may not exceed
3133 * five digits.
3134 * @param addr The string to search for addresses.
3135 * @param caseInsensitive addr Set to true to make search ignore case.
3136 *
3137 * @return the address, or if no address is found, return null.
3138 */
3139 public static String findAddress(String addr, boolean caseInsensitive) {
3140 return WebViewCore.nativeFindAddress(addr, caseInsensitive);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003141 }
3142
3143 /*
3144 * Clear the highlighting surrounding text matches created by findAll.
3145 */
3146 public void clearMatches() {
Cary Clark32847a92009-11-17 16:04:18 -05003147 if (mNativeClass == 0)
3148 return;
Cary Clarkde023c12010-03-03 10:05:16 -05003149 nativeSetFindIsEmpty();
3150 invalidate();
3151 }
3152
3153 /**
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003154 * Called when the find ActionMode ends.
Cary Clarkde023c12010-03-03 10:05:16 -05003155 */
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003156 void notifyFindDialogDismissed() {
3157 mFindCallback = null;
Shimeng (Simon) Wang4d8ef422010-03-22 17:06:17 -07003158 if (mWebViewCore == null) {
3159 return;
3160 }
Cary Clarkde023c12010-03-03 10:05:16 -05003161 clearMatches();
3162 setFindIsUp(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003163 // Now that the dialog has been removed, ensure that we scroll to a
3164 // location that is not beyond the end of the page.
3165 pinScrollTo(mScrollX, mScrollY, false, 0);
3166 invalidate();
3167 }
3168
3169 /**
3170 * Query the document to see if it contains any image references. The
3171 * message object will be dispatched with arg1 being set to 1 if images
3172 * were found and 0 if the document does not reference any images.
3173 * @param response The message that will be dispatched with the result.
3174 */
3175 public void documentHasImages(Message response) {
3176 if (response == null) {
3177 return;
3178 }
3179 mWebViewCore.sendMessage(EventHub.DOC_HAS_IMAGES, response);
3180 }
3181
Michael Kolb73980a92010-08-05 16:32:51 -07003182 /**
3183 * Request the scroller to abort any ongoing animation
3184 *
3185 * @hide
3186 */
3187 public void stopScroll() {
3188 mScroller.forceFinished(true);
3189 mLastVelocity = 0;
3190 }
3191
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003192 @Override
3193 public void computeScroll() {
3194 if (mScroller.computeScrollOffset()) {
3195 int oldX = mScrollX;
3196 int oldY = mScrollY;
Adam Powell637d3372010-08-25 14:37:03 -07003197 int x = mScroller.getCurrX();
3198 int y = mScroller.getCurrY();
3199 invalidate(); // So we draw again
3200
3201 if (!mScroller.isFinished()) {
Patrick Scott62310912010-12-06 17:44:50 -05003202 int rangeX = computeMaxScrollX();
3203 int rangeY = computeMaxScrollY();
3204 int overflingDistance = mOverflingDistance;
3205
3206 // Use the layer's scroll data if needed.
3207 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
3208 oldX = mScrollingLayerRect.left;
3209 oldY = mScrollingLayerRect.top;
3210 rangeX = mScrollingLayerRect.right;
3211 rangeY = mScrollingLayerRect.bottom;
3212 // No overscrolling for layers.
3213 overflingDistance = 0;
3214 }
3215
Adam Powell637d3372010-08-25 14:37:03 -07003216 overScrollBy(x - oldX, y - oldY, oldX, oldY,
3217 rangeX, rangeY,
Patrick Scott62310912010-12-06 17:44:50 -05003218 overflingDistance, overflingDistance, false);
Adam Powell637d3372010-08-25 14:37:03 -07003219
3220 if (mOverScrollGlow != null) {
3221 mOverScrollGlow.absorbGlow(x, y, oldX, oldY, rangeX, rangeY);
3222 }
3223 } else {
Patrick Scott62310912010-12-06 17:44:50 -05003224 if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
3225 mScrollX = x;
3226 mScrollY = y;
3227 } else {
3228 // Update the layer position instead of WebView.
3229 nativeScrollLayer(mScrollingLayer, x, y);
3230 mScrollingLayerRect.left = x;
3231 mScrollingLayerRect.top = y;
3232 }
Adam Powell4c3ce842010-11-23 17:39:56 -08003233 abortAnimation();
3234 mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
3235 WebViewCore.resumePriority();
Cary Clark0df02692010-11-24 11:01:37 -05003236 if (!mSelectingText) {
3237 WebViewCore.resumeUpdatePicture(mWebViewCore);
3238 }
Patrick Scottfa8be1c2011-02-02 14:09:34 -05003239 if (oldX != mScrollX || oldY != mScrollY) {
3240 sendOurVisibleRect();
3241 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003242 }
3243 } else {
3244 super.computeScroll();
3245 }
3246 }
3247
3248 private static int computeDuration(int dx, int dy) {
3249 int distance = Math.max(Math.abs(dx), Math.abs(dy));
3250 int duration = distance * 1000 / STD_SPEED;
3251 return Math.min(duration, MAX_DURATION);
3252 }
3253
3254 // helper to pin the scrollBy parameters (already in view coordinates)
3255 // returns true if the scroll was changed
3256 private boolean pinScrollBy(int dx, int dy, boolean animate, int animationDuration) {
3257 return pinScrollTo(mScrollX + dx, mScrollY + dy, animate, animationDuration);
3258 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003259 // helper to pin the scrollTo parameters (already in view coordinates)
3260 // returns true if the scroll was changed
3261 private boolean pinScrollTo(int x, int y, boolean animate, int animationDuration) {
3262 x = pinLocX(x);
3263 y = pinLocY(y);
3264 int dx = x - mScrollX;
3265 int dy = y - mScrollY;
3266
3267 if ((dx | dy) == 0) {
3268 return false;
3269 }
Cary Clark21a97ef2010-11-16 09:59:40 -05003270 abortAnimation();
Leon Scrogginsd55de402009-09-17 14:19:49 -04003271 if (animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003272 // Log.d(LOGTAG, "startScroll: " + dx + " " + dy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003273 mScroller.startScroll(mScrollX, mScrollY, dx, dy,
3274 animationDuration > 0 ? animationDuration : computeDuration(dx, dy));
Mike Cleronf116bf82009-09-27 19:14:12 -07003275 awakenScrollBars(mScroller.getDuration());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003276 invalidate();
3277 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003278 scrollTo(x, y);
3279 }
3280 return true;
3281 }
3282
3283 // Scale from content to view coordinates, and pin.
3284 // Also called by jni webview.cpp
Leon Scroggins4c943042009-07-31 15:05:42 -04003285 private boolean setContentScrollBy(int cx, int cy, boolean animate) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003286 if (mDrawHistory) {
3287 // disallow WebView to change the scroll position as History Picture
3288 // is used in the view system.
3289 // TODO: as we switchOutDrawHistory when trackball or navigation
3290 // keys are hit, this should be safe. Right?
Leon Scroggins4c943042009-07-31 15:05:42 -04003291 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003292 }
Mike Reede8853fc2009-09-04 14:01:48 -04003293 cx = contentToViewDimension(cx);
3294 cy = contentToViewDimension(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003295 if (mHeightCanMeasure) {
3296 // move our visible rect according to scroll request
3297 if (cy != 0) {
3298 Rect tempRect = new Rect();
3299 calcOurVisibleRect(tempRect);
3300 tempRect.offset(cx, cy);
3301 requestRectangleOnScreen(tempRect);
3302 }
3303 // FIXME: We scroll horizontally no matter what because currently
3304 // ScrollView and ListView will not scroll horizontally.
3305 // FIXME: Why do we only scroll horizontally if there is no
3306 // vertical scroll?
3307// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
Leon Scroggins4c943042009-07-31 15:05:42 -04003308 return cy == 0 && cx != 0 && pinScrollBy(cx, 0, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003309 } else {
Leon Scroggins4c943042009-07-31 15:05:42 -04003310 return pinScrollBy(cx, cy, animate, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003311 }
3312 }
3313
Leon Scroggins405d7852009-11-02 14:50:54 -08003314 /**
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07003315 * Called by CallbackProxy when the page starts loading.
3316 * @param url The URL of the page which has started loading.
3317 */
3318 /* package */ void onPageStarted(String url) {
3319 // every time we start a new page, we want to reset the
3320 // WebView certificate: if the new site is secure, we
3321 // will reload it and get a new certificate set;
3322 // if the new site is not secure, the certificate must be
3323 // null, and that will be the case
3324 setCertificate(null);
3325
3326 // reset the flag since we set to true in if need after
3327 // loading is see onPageFinished(Url)
3328 mAccessibilityScriptInjected = false;
3329 }
3330
3331 /**
Leon Scroggins405d7852009-11-02 14:50:54 -08003332 * Called by CallbackProxy when the page finishes loading.
3333 * @param url The URL of the page which has finished loading.
3334 */
3335 /* package */ void onPageFinished(String url) {
3336 if (mPageThatNeedsToSlideTitleBarOffScreen != null) {
3337 // If the user is now on a different page, or has scrolled the page
3338 // past the point where the title bar is offscreen, ignore the
3339 // scroll request.
3340 if (mPageThatNeedsToSlideTitleBarOffScreen.equals(url)
3341 && mScrollX == 0 && mScrollY == 0) {
3342 pinScrollTo(0, mYDistanceToSlideTitleOffScreen, true,
3343 SLIDE_TITLE_DURATION);
3344 }
3345 mPageThatNeedsToSlideTitleBarOffScreen = null;
3346 }
Shimeng (Simon) Wangd75fd492011-02-16 14:52:13 -08003347 mZoomManager.onPageFinished(url);
Svetoslav Ganovda355512010-05-12 22:04:44 -07003348 injectAccessibilityForUrl(url);
3349 }
3350
3351 /**
3352 * This method injects accessibility in the loaded document if accessibility
3353 * is enabled. If JavaScript is enabled we try to inject a URL specific script.
3354 * If no URL specific script is found or JavaScript is disabled we fallback to
3355 * the default {@link AccessibilityInjector} implementation.
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07003356 * </p>
3357 * If the URL has the "axs" paramter set to 1 it has already done the
3358 * script injection so we do nothing. If the parameter is set to 0
3359 * the URL opts out accessibility script injection so we fall back to
3360 * the default {@link AccessibilityInjector}.
3361 * </p>
3362 * Note: If the user has not opted-in the accessibility script injection no scripts
3363 * are injected rather the default {@link AccessibilityInjector} implementation
3364 * is used.
Svetoslav Ganovda355512010-05-12 22:04:44 -07003365 *
3366 * @param url The URL loaded by this {@link WebView}.
3367 */
3368 private void injectAccessibilityForUrl(String url) {
Svetoslav Ganov12bed782011-01-03 14:14:50 -08003369 if (mWebViewCore == null) {
3370 return;
3371 }
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07003372 AccessibilityManager accessibilityManager = AccessibilityManager.getInstance(mContext);
3373
3374 if (!accessibilityManager.isEnabled()) {
Svetoslav Ganovda355512010-05-12 22:04:44 -07003375 // it is possible that accessibility was turned off between reloads
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07003376 ensureAccessibilityScriptInjectorInstance(false);
3377 return;
3378 }
3379
3380 if (!getSettings().getJavaScriptEnabled()) {
3381 // no JS so we fallback to the basic buil-in support
3382 ensureAccessibilityScriptInjectorInstance(true);
3383 return;
3384 }
3385
3386 // check the URL "axs" parameter to choose appropriate action
3387 int axsParameterValue = getAxsUrlParameterValue(url);
3388 if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_UNDEFINED) {
3389 boolean onDeviceScriptInjectionEnabled = (Settings.Secure.getInt(mContext
3390 .getContentResolver(), Settings.Secure.ACCESSIBILITY_SCRIPT_INJECTION, 0) == 1);
3391 if (onDeviceScriptInjectionEnabled) {
3392 ensureAccessibilityScriptInjectorInstance(false);
3393 // neither script injected nor script injection opted out => we inject
3394 loadUrl(ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT);
3395 // TODO: Set this flag after successfull script injection. Maybe upon injection
3396 // the chooser should update the meta tag and we check it to declare success
3397 mAccessibilityScriptInjected = true;
3398 } else {
3399 // injection disabled so we fallback to the basic built-in support
3400 ensureAccessibilityScriptInjectorInstance(true);
3401 }
3402 } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_OPTED_OUT) {
3403 // injection opted out so we fallback to the basic buil-in support
3404 ensureAccessibilityScriptInjectorInstance(true);
3405 } else if (axsParameterValue == ACCESSIBILITY_SCRIPT_INJECTION_PROVIDED) {
3406 ensureAccessibilityScriptInjectorInstance(false);
3407 // the URL provides accessibility but we still need to add our generic script
3408 loadUrl(ACCESSIBILITY_SCRIPT_CHOOSER_JAVASCRIPT);
3409 } else {
3410 Log.e(LOGTAG, "Unknown URL value for the \"axs\" URL parameter: " + axsParameterValue);
3411 }
3412 }
3413
3414 /**
3415 * Ensures the instance of the {@link AccessibilityInjector} to be present ot not.
3416 *
3417 * @param present True to ensure an insance, false to ensure no instance.
3418 */
3419 private void ensureAccessibilityScriptInjectorInstance(boolean present) {
Svetoslav Ganov12bed782011-01-03 14:14:50 -08003420 if (present) {
3421 if (mAccessibilityInjector == null) {
3422 mAccessibilityInjector = new AccessibilityInjector(this);
3423 }
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07003424 } else {
Svetoslav Ganovda355512010-05-12 22:04:44 -07003425 mAccessibilityInjector = null;
3426 }
Leon Scroggins405d7852009-11-02 14:50:54 -08003427 }
3428
3429 /**
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07003430 * Gets the "axs" URL parameter value.
3431 *
3432 * @param url A url to fetch the paramter from.
3433 * @return The parameter value if such, -1 otherwise.
3434 */
3435 private int getAxsUrlParameterValue(String url) {
3436 if (mMatchAxsUrlParameterPattern == null) {
3437 mMatchAxsUrlParameterPattern = Pattern.compile(PATTERN_MATCH_AXS_URL_PARAMETER);
3438 }
3439 Matcher matcher = mMatchAxsUrlParameterPattern.matcher(url);
3440 if (matcher.find()) {
3441 String keyValuePair = url.substring(matcher.start(), matcher.end());
3442 return Integer.parseInt(keyValuePair.split("=")[1]);
3443 }
3444 return -1;
3445 }
3446
3447 /**
Leon Scroggins405d7852009-11-02 14:50:54 -08003448 * The URL of a page that sent a message to scroll the title bar off screen.
3449 *
3450 * Many mobile sites tell the page to scroll to (0,1) in order to scroll the
3451 * title bar off the screen. Sometimes, the scroll position is set before
3452 * the page finishes loading. Rather than scrolling while the page is still
3453 * loading, keep track of the URL and new scroll position so we can perform
3454 * the scroll once the page finishes loading.
3455 */
3456 private String mPageThatNeedsToSlideTitleBarOffScreen;
3457
3458 /**
3459 * The destination Y scroll position to be used when the page finishes
3460 * loading. See mPageThatNeedsToSlideTitleBarOffScreen.
3461 */
3462 private int mYDistanceToSlideTitleOffScreen;
3463
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003464 // scale from content to view coordinates, and pin
Leon Scroggins83d4ba82009-09-17 17:27:13 -04003465 // return true if pin caused the final x/y different than the request cx/cy,
3466 // and a future scroll may reach the request cx/cy after our size has
3467 // changed
3468 // return false if the view scroll to the exact position as it is requested,
3469 // where negative numbers are taken to mean 0
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003470 private boolean setContentScrollTo(int cx, int cy) {
3471 if (mDrawHistory) {
3472 // disallow WebView to change the scroll position as History Picture
3473 // is used in the view system.
3474 // One known case where this is called is that WebCore tries to
3475 // restore the scroll position. As history Picture already uses the
3476 // saved scroll position, it is ok to skip this.
3477 return false;
3478 }
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04003479 int vx;
3480 int vy;
3481 if ((cx | cy) == 0) {
3482 // If the page is being scrolled to (0,0), do not add in the title
3483 // bar's height, and simply scroll to (0,0). (The only other work
3484 // in contentToView_ is to multiply, so this would not change 0.)
3485 vx = 0;
3486 vy = 0;
3487 } else {
3488 vx = contentToViewX(cx);
3489 vy = contentToViewY(cy);
3490 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003491// Log.d(LOGTAG, "content scrollTo [" + cx + " " + cy + "] view=[" +
3492// vx + " " + vy + "]");
Leon Scroggins03c87bf2009-09-18 15:05:59 -04003493 // Some mobile sites attempt to scroll the title bar off the page by
Leon Scrogginsd7b95aa2009-09-21 13:27:02 -04003494 // scrolling to (0,1). If we are at the top left corner of the
Leon Scroggins03c87bf2009-09-18 15:05:59 -04003495 // page, assume this is an attempt to scroll off the title bar, and
3496 // animate the title bar off screen slowly enough that the user can see
3497 // it.
Leon Scroggins405d7852009-11-02 14:50:54 -08003498 if (cx == 0 && cy == 1 && mScrollX == 0 && mScrollY == 0
3499 && mTitleBar != null) {
3500 // FIXME: 100 should be defined somewhere as our max progress.
3501 if (getProgress() < 100) {
3502 // Wait to scroll the title bar off screen until the page has
3503 // finished loading. Keep track of the URL and the destination
3504 // Y position
3505 mPageThatNeedsToSlideTitleBarOffScreen = getUrl();
3506 mYDistanceToSlideTitleOffScreen = vy;
3507 } else {
3508 pinScrollTo(vx, vy, true, SLIDE_TITLE_DURATION);
3509 }
Leon Scroggins03c87bf2009-09-18 15:05:59 -04003510 // Since we are animating, we have not yet reached the desired
3511 // scroll position. Do not return true to request another attempt
3512 return false;
3513 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003514 pinScrollTo(vx, vy, false, 0);
Leon Scroggins83d4ba82009-09-17 17:27:13 -04003515 // If the request was to scroll to a negative coordinate, treat it as if
3516 // it was a request to scroll to 0
3517 if ((mScrollX != vx && cx >= 0) || (mScrollY != vy && cy >= 0)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003518 return true;
3519 } else {
3520 return false;
3521 }
3522 }
3523
3524 // scale from content to view coordinates, and pin
3525 private void spawnContentScrollTo(int cx, int cy) {
3526 if (mDrawHistory) {
3527 // disallow WebView to change the scroll position as History Picture
3528 // is used in the view system.
3529 return;
3530 }
Leon Scroggins0236e672009-09-02 21:12:08 -04003531 int vx = contentToViewX(cx);
3532 int vy = contentToViewY(cy);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003533 pinScrollTo(vx, vy, true, 0);
3534 }
3535
3536 /**
3537 * These are from webkit, and are in content coordinate system (unzoomed)
3538 */
3539 private void contentSizeChanged(boolean updateLayout) {
3540 // suppress 0,0 since we usually see real dimensions soon after
3541 // this avoids drawing the prev content in a funny place. If we find a
3542 // way to consolidate these notifications, this check may become
3543 // obsolete
3544 if ((mContentWidth | mContentHeight) == 0) {
3545 return;
3546 }
3547
3548 if (mHeightCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04003549 if (getMeasuredHeight() != contentToViewDimension(mContentHeight)
Grace Kloba3f9faf42009-10-13 14:13:54 -07003550 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003551 requestLayout();
3552 }
3553 } else if (mWidthCanMeasure) {
Mike Reede8853fc2009-09-04 14:01:48 -04003554 if (getMeasuredWidth() != contentToViewDimension(mContentWidth)
Grace Kloba3f9faf42009-10-13 14:13:54 -07003555 || updateLayout) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003556 requestLayout();
3557 }
3558 } else {
3559 // If we don't request a layout, try to send our view size to the
3560 // native side to ensure that WebCore has the correct dimensions.
Derek Sollenberger03e48912010-05-18 17:03:42 -04003561 sendViewSizeZoom(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003562 }
3563 }
3564
3565 /**
3566 * Set the WebViewClient that will receive various notifications and
3567 * requests. This will replace the current handler.
3568 * @param client An implementation of WebViewClient.
3569 */
3570 public void setWebViewClient(WebViewClient client) {
3571 mCallbackProxy.setWebViewClient(client);
3572 }
3573
3574 /**
Grace Kloba94ab3b62009-10-07 18:00:19 -07003575 * Gets the WebViewClient
3576 * @return the current WebViewClient instance.
3577 *
3578 *@hide pending API council approval.
3579 */
3580 public WebViewClient getWebViewClient() {
3581 return mCallbackProxy.getWebViewClient();
3582 }
3583
3584 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003585 * Register the interface to be used when content can not be handled by
3586 * the rendering engine, and should be downloaded instead. This will replace
3587 * the current handler.
3588 * @param listener An implementation of DownloadListener.
3589 */
3590 public void setDownloadListener(DownloadListener listener) {
3591 mCallbackProxy.setDownloadListener(listener);
3592 }
3593
3594 /**
3595 * Set the chrome handler. This is an implementation of WebChromeClient for
Steve Block81f19ff2010-11-01 13:23:24 +00003596 * use in handling JavaScript dialogs, favicons, titles, and the progress.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003597 * This will replace the current handler.
3598 * @param client An implementation of WebChromeClient.
3599 */
3600 public void setWebChromeClient(WebChromeClient client) {
3601 mCallbackProxy.setWebChromeClient(client);
3602 }
3603
3604 /**
Andrei Popescu6fa29582009-06-19 14:54:09 +01003605 * Gets the chrome handler.
3606 * @return the current WebChromeClient instance.
3607 *
3608 * @hide API council approval.
3609 */
3610 public WebChromeClient getWebChromeClient() {
3611 return mCallbackProxy.getWebChromeClient();
3612 }
3613
3614 /**
Patrick Scott0b2e84b2010-03-02 08:58:44 -05003615 * Set the back/forward list client. This is an implementation of
3616 * WebBackForwardListClient for handling new items and changes in the
3617 * history index.
3618 * @param client An implementation of WebBackForwardListClient.
3619 * {@hide}
3620 */
3621 public void setWebBackForwardListClient(WebBackForwardListClient client) {
3622 mCallbackProxy.setWebBackForwardListClient(client);
3623 }
3624
3625 /**
3626 * Gets the WebBackForwardListClient.
3627 * {@hide}
3628 */
3629 public WebBackForwardListClient getWebBackForwardListClient() {
3630 return mCallbackProxy.getWebBackForwardListClient();
3631 }
3632
3633 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003634 * Set the Picture listener. This is an interface used to receive
3635 * notifications of a new Picture.
3636 * @param listener An implementation of WebView.PictureListener.
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00003637 * @deprecated This method is now obsolete.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003638 */
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00003639 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003640 public void setPictureListener(PictureListener listener) {
3641 mPictureListener = listener;
3642 }
3643
3644 /**
3645 * {@hide}
3646 */
3647 /* FIXME: Debug only! Remove for SDK! */
3648 public void externalRepresentation(Message callback) {
3649 mWebViewCore.sendMessage(EventHub.REQUEST_EXT_REPRESENTATION, callback);
3650 }
3651
3652 /**
3653 * {@hide}
3654 */
3655 /* FIXME: Debug only! Remove for SDK! */
3656 public void documentAsText(Message callback) {
3657 mWebViewCore.sendMessage(EventHub.REQUEST_DOC_AS_TEXT, callback);
3658 }
3659
3660 /**
Steve Block81f19ff2010-11-01 13:23:24 +00003661 * Use this function to bind an object to JavaScript so that the
3662 * methods can be accessed from JavaScript.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003663 * <p><strong>IMPORTANT:</strong>
3664 * <ul>
3665 * <li> Using addJavascriptInterface() allows JavaScript to control your
3666 * application. This can be a very useful feature or a dangerous security
3667 * issue. When the HTML in the WebView is untrustworthy (for example, part
3668 * or all of the HTML is provided by some person or process), then an
3669 * attacker could inject HTML that will execute your code and possibly any
3670 * code of the attacker's choosing.<br>
3671 * Do not use addJavascriptInterface() unless all of the HTML in this
3672 * WebView was written by you.</li>
3673 * <li> The Java object that is bound runs in another thread and not in
3674 * the thread that it was constructed in.</li>
3675 * </ul></p>
Steve Block81f19ff2010-11-01 13:23:24 +00003676 * @param obj The class instance to bind to JavaScript, null instances are
Steve Block544295e2010-12-02 18:40:06 +00003677 * ignored.
Steve Block689a3422010-12-07 18:18:26 +00003678 * @param interfaceName The name to used to expose the instance in
3679 * JavaScript.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003680 */
3681 public void addJavascriptInterface(Object obj, String interfaceName) {
Steve Block544295e2010-12-02 18:40:06 +00003682 if (obj == null) {
3683 return;
3684 }
Cary Clarkded054c2009-06-15 10:26:08 -04003685 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
3686 arg.mObject = obj;
3687 arg.mInterfaceName = interfaceName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003688 mWebViewCore.sendMessage(EventHub.ADD_JS_INTERFACE, arg);
3689 }
3690
3691 /**
Steve Block689a3422010-12-07 18:18:26 +00003692 * Removes a previously added JavaScript interface with the given name.
3693 * @param interfaceName The name of the interface to remove.
3694 */
3695 public void removeJavascriptInterface(String interfaceName) {
Svetoslav Ganovfa443142011-03-02 14:54:03 -08003696 if (mWebViewCore != null) {
3697 WebViewCore.JSInterfaceData arg = new WebViewCore.JSInterfaceData();
3698 arg.mInterfaceName = interfaceName;
3699 mWebViewCore.sendMessage(EventHub.REMOVE_JS_INTERFACE, arg);
3700 }
Steve Block689a3422010-12-07 18:18:26 +00003701 }
3702
3703 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003704 * Return the WebSettings object used to control the settings for this
3705 * WebView.
3706 * @return A WebSettings object that can be used to control this WebView's
3707 * settings.
3708 */
3709 public WebSettings getSettings() {
Derek Sollenberger90b6e482010-05-10 12:38:54 -04003710 return (mWebViewCore != null) ? mWebViewCore.getSettings() : null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003711 }
3712
3713 /**
3714 * Return the list of currently loaded plugins.
3715 * @return The list of currently loaded plugins.
Andrei Popescu385df692009-08-13 11:59:57 +01003716 *
Kristian Monsenf0d97312011-01-12 19:15:35 +00003717 * @hide
Andrei Popescu385df692009-08-13 11:59:57 +01003718 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003719 */
Andrei Popescu385df692009-08-13 11:59:57 +01003720 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003721 public static synchronized PluginList getPluginList() {
Grace Klobabb245ea2009-11-10 13:13:24 -08003722 return new PluginList();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003723 }
3724
3725 /**
Kristian Monsenf0d97312011-01-12 19:15:35 +00003726 * @hide
Andrei Popescu385df692009-08-13 11:59:57 +01003727 * @deprecated This was used for Gears, which has been deprecated.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003728 */
Andrei Popescu385df692009-08-13 11:59:57 +01003729 @Deprecated
3730 public void refreshPlugins(boolean reloadOpenPages) { }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003731
3732 //-------------------------------------------------------------------------
3733 // Override View methods
3734 //-------------------------------------------------------------------------
3735
3736 @Override
3737 protected void finalize() throws Throwable {
Cary Clark9a4c0632009-08-10 16:40:08 -04003738 try {
3739 destroy();
3740 } finally {
3741 super.finalize();
3742 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003743 }
Cary Clarkd6982c92009-05-29 11:02:22 -04003744
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003745 @Override
Leon Scroggins0236e672009-09-02 21:12:08 -04003746 protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
3747 if (child == mTitleBar) {
3748 // When drawing the title bar, move it horizontally to always show
Adam Powell9d32d242010-03-29 16:02:07 -07003749 // at the top of the WebView.
Leon Scroggins0236e672009-09-02 21:12:08 -04003750 mTitleBar.offsetLeftAndRight(mScrollX - mTitleBar.getLeft());
Michael Kolbc4ca53f2011-02-05 13:53:14 -08003751 int newTop = 0;
3752 if (mTitleGravity == Gravity.NO_GRAVITY) {
3753 newTop = Math.min(0, mScrollY);
3754 } else if (mTitleGravity == Gravity.TOP) {
3755 newTop = mScrollY;
3756 }
Michael Kolb75a03f92011-02-23 16:12:13 -08003757 mTitleBar.setBottom(newTop + mTitleBar.getHeight());
Mindy Pereira3331f2b2010-12-03 09:58:37 -08003758 mTitleBar.setTop(newTop);
Leon Scroggins0236e672009-09-02 21:12:08 -04003759 }
3760 return super.drawChild(canvas, child, drawingTime);
3761 }
3762
Mike Reed19f3f0e2009-11-12 12:50:20 -05003763 private void drawContent(Canvas canvas) {
Grace Kloba04b28682009-09-14 14:38:37 -07003764 // Update the buttons in the picture, so when we draw the picture
3765 // to the screen, they are in the correct state.
3766 // Tell the native side if user is a) touching the screen,
3767 // b) pressing the trackball down, or c) pressing the enter key
3768 // If the cursor is on a button, we need to draw it in the pressed
3769 // state.
3770 // If mNativeClass is 0, we should not reach here, so we do not
3771 // need to check it again.
3772 nativeRecordButtons(hasFocus() && hasWindowFocus(),
Mike Reed19f3f0e2009-11-12 12:50:20 -05003773 mTouchMode == TOUCH_SHORTPRESS_START_MODE
3774 || mTrackballDown || mGotCenterDown, false);
Grace Kloba04b28682009-09-14 14:38:37 -07003775 drawCoreAndCursorRing(canvas, mBackgroundColor, mDrawCursorRing);
Mike Reed19f3f0e2009-11-12 12:50:20 -05003776 }
3777
Adam Powell637d3372010-08-25 14:37:03 -07003778 /**
3779 * Draw the background when beyond bounds
3780 * @param canvas Canvas to draw into
3781 */
3782 private void drawOverScrollBackground(Canvas canvas) {
3783 if (mOverScrollBackground == null) {
3784 mOverScrollBackground = new Paint();
3785 Bitmap bm = BitmapFactory.decodeResource(
3786 mContext.getResources(),
3787 com.android.internal.R.drawable.status_bar_background);
3788 mOverScrollBackground.setShader(new BitmapShader(bm,
3789 Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
3790 mOverScrollBorder = new Paint();
3791 mOverScrollBorder.setStyle(Paint.Style.STROKE);
3792 mOverScrollBorder.setStrokeWidth(0);
3793 mOverScrollBorder.setColor(0xffbbbbbb);
3794 }
3795
3796 int top = 0;
3797 int right = computeRealHorizontalScrollRange();
3798 int bottom = top + computeRealVerticalScrollRange();
3799 // first draw the background and anchor to the top of the view
3800 canvas.save();
3801 canvas.translate(mScrollX, mScrollY);
3802 canvas.clipRect(-mScrollX, top - mScrollY, right - mScrollX, bottom
3803 - mScrollY, Region.Op.DIFFERENCE);
3804 canvas.drawPaint(mOverScrollBackground);
3805 canvas.restore();
3806 // then draw the border
3807 canvas.drawRect(-1, top - 1, right, bottom, mOverScrollBorder);
3808 // next clip the region for the content
3809 canvas.clipRect(0, top, right, bottom);
3810 }
3811
Mike Reed19f3f0e2009-11-12 12:50:20 -05003812 @Override
3813 protected void onDraw(Canvas canvas) {
3814 // if mNativeClass is 0, the WebView has been destroyed. Do nothing.
3815 if (mNativeClass == 0) {
3816 return;
3817 }
3818
Grace Klobae47ac472010-03-11 14:53:07 -08003819 // if both mContentWidth and mContentHeight are 0, it means there is no
3820 // valid Picture passed to WebView yet. This can happen when WebView
3821 // just starts. Draw the background and return.
3822 if ((mContentWidth | mContentHeight) == 0 && mHistoryPicture == null) {
3823 canvas.drawColor(mBackgroundColor);
3824 return;
3825 }
3826
Ben Murdoch811ba6c2011-01-26 10:19:39 +00003827 if (canvas.isHardwareAccelerated()) {
3828 mZoomManager.setHardwareAccelerated();
3829 }
3830
Mike Reed19f3f0e2009-11-12 12:50:20 -05003831 int saveCount = canvas.save();
Adam Powell637d3372010-08-25 14:37:03 -07003832 if (mInOverScrollMode && !getSettings()
3833 .getUseWebViewBackgroundForOverscrollBackground()) {
3834 drawOverScrollBackground(canvas);
3835 }
Mike Reed19f3f0e2009-11-12 12:50:20 -05003836 if (mTitleBar != null) {
Michael Kolb75a03f92011-02-23 16:12:13 -08003837 canvas.translate(0, getTitleHeight());
Mike Reed19f3f0e2009-11-12 12:50:20 -05003838 }
Bjorn Bringertc9332fa2010-10-13 17:24:27 +01003839 drawContent(canvas);
Leon Scroggins0236e672009-09-02 21:12:08 -04003840 canvas.restoreToCount(saveCount);
Cary Clarkd6982c92009-05-29 11:02:22 -04003841
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003842 if (AUTO_REDRAW_HACK && mAutoRedraw) {
3843 invalidate();
3844 }
Leon Scroggins56426252010-11-01 15:45:37 -04003845 if (inEditingMode()) {
3846 mWebTextView.onDrawSubstitute();
3847 }
Nicolas Roard38863332010-01-04 19:30:55 +00003848 mWebViewCore.signalRepaintDone();
Grace Kloba178db412010-05-18 22:22:23 -07003849
Adam Powell637d3372010-08-25 14:37:03 -07003850 if (mOverScrollGlow != null && mOverScrollGlow.drawEdgeGlows(canvas)) {
3851 invalidate();
3852 }
3853
Grace Kloba178db412010-05-18 22:22:23 -07003854 // paint the highlight in the end
3855 if (!mTouchHighlightRegion.isEmpty()) {
3856 if (mTouchHightlightPaint == null) {
3857 mTouchHightlightPaint = new Paint();
3858 mTouchHightlightPaint.setColor(mHightlightColor);
3859 mTouchHightlightPaint.setAntiAlias(true);
3860 mTouchHightlightPaint.setPathEffect(new CornerPathEffect(
3861 TOUCH_HIGHLIGHT_ARC));
3862 }
3863 canvas.drawPath(mTouchHighlightRegion.getBoundaryPath(),
3864 mTouchHightlightPaint);
3865 }
3866 if (DEBUG_TOUCH_HIGHLIGHT) {
3867 if (getSettings().getNavDump()) {
3868 if ((mTouchHighlightX | mTouchHighlightY) != 0) {
3869 if (mTouchCrossHairColor == null) {
3870 mTouchCrossHairColor = new Paint();
3871 mTouchCrossHairColor.setColor(Color.RED);
3872 }
3873 canvas.drawLine(mTouchHighlightX - mNavSlop,
3874 mTouchHighlightY - mNavSlop, mTouchHighlightX
3875 + mNavSlop + 1, mTouchHighlightY + mNavSlop
3876 + 1, mTouchCrossHairColor);
3877 canvas.drawLine(mTouchHighlightX + mNavSlop + 1,
3878 mTouchHighlightY - mNavSlop, mTouchHighlightX
3879 - mNavSlop,
3880 mTouchHighlightY + mNavSlop + 1,
3881 mTouchCrossHairColor);
3882 }
3883 }
3884 }
3885 }
3886
3887 private void removeTouchHighlight(boolean removePendingMessage) {
3888 if (removePendingMessage) {
3889 mWebViewCore.removeMessages(EventHub.GET_TOUCH_HIGHLIGHT_RECTS);
3890 }
3891 mWebViewCore.sendMessage(EventHub.REMOVE_TOUCH_HIGHLIGHT_RECTS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003892 }
3893
3894 @Override
3895 public void setLayoutParams(ViewGroup.LayoutParams params) {
3896 if (params.height == LayoutParams.WRAP_CONTENT) {
3897 mWrapContent = true;
3898 }
3899 super.setLayoutParams(params);
3900 }
3901
3902 @Override
3903 public boolean performLongClick() {
Grace Kloba98e6fcf2010-01-27 15:20:30 -08003904 // performLongClick() is the result of a delayed message. If we switch
3905 // to windows overview, the WebView will be temporarily removed from the
3906 // view system. In that case, do nothing.
3907 if (getParent() == null) return false;
Adam Powellbe366682010-09-12 12:47:04 -07003908
3909 // A multi-finger gesture can look like a long press; make sure we don't take
3910 // long press actions if we're scaling.
3911 final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
3912 if (detector != null && detector.isInProgress()) {
3913 return false;
3914 }
Michael Kolbc4ca53f2011-02-05 13:53:14 -08003915
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003916 if (mNativeClass != 0 && nativeCursorIsTextInput()) {
3917 // Send the click so that the textfield is in focus
Leon Scroggins1d96ca02009-10-23 11:49:03 -04003918 centerKeyPressOnTextField();
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003919 rebuildWebTextView();
Leon Scroggins2c8e0512010-05-11 15:50:27 -04003920 } else {
Leon Scroggins2aed7762010-08-13 17:11:42 -04003921 clearTextEntry();
Leon Scroggins3ccd3652009-06-26 17:22:50 -04003922 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003923 if (inEditingMode()) {
Leon Scrogginsfcf57762010-12-14 18:09:23 -05003924 // Since we just called rebuildWebTextView, the layout is not set
3925 // properly. Update it so it can correctly find the word to select.
3926 mWebTextView.ensureLayout();
3927 // Provide a touch down event to WebTextView, which will allow it
3928 // to store the location to use in performLongClick.
3929 AbsoluteLayout.LayoutParams params
3930 = (AbsoluteLayout.LayoutParams) mWebTextView.getLayoutParams();
3931 MotionEvent fake = MotionEvent.obtain(mLastTouchTime,
3932 mLastTouchTime, MotionEvent.ACTION_DOWN,
3933 mLastTouchX - params.x + mScrollX,
3934 mLastTouchY - params.y + mScrollY, 0);
3935 mWebTextView.dispatchTouchEvent(fake);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04003936 return mWebTextView.performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003937 }
Cary Clark1e8e10d2010-11-17 13:49:25 -05003938 if (mSelectingText) return false; // long click does nothing on selection
Cary Clark924af702010-06-04 16:37:43 -04003939 /* if long click brings up a context menu, the super function
3940 * returns true and we're done. Otherwise, nothing happened when
3941 * the user clicked. */
3942 if (super.performLongClick()) {
3943 return true;
3944 }
3945 /* In the case where the application hasn't already handled the long
3946 * click action, look for a word under the click. If one is found,
3947 * animate the text selection into view.
3948 * FIXME: no animation code yet */
Cary Clarkfc8bf742010-11-22 11:02:29 -05003949 return selectText();
3950 }
3951
3952 /**
3953 * Select the word at the last click point.
3954 *
3955 * @hide pending API council approval
3956 */
3957 public boolean selectText() {
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08003958 int x = viewToContentX(mLastTouchX + mScrollX);
3959 int y = viewToContentY(mLastTouchY + mScrollY);
Cary Clark2cdee232010-12-29 14:59:24 -05003960 return selectText(x, y);
3961 }
3962
3963 /**
3964 * Select the word at the indicated content coordinates.
3965 */
3966 boolean selectText(int x, int y) {
Cary Clark03f00222011-02-10 19:37:15 -05003967 if (!setUpSelect(true, x, y)) {
Leon Scroggins8a4fd2f2010-12-16 18:17:23 -05003968 return false;
3969 }
Cary Clark03f00222011-02-10 19:37:15 -05003970 nativeSetExtendSelection();
3971 mDrawSelectionPointer = false;
3972 mSelectionStarted = true;
3973 mTouchMode = TOUCH_DRAG_MODE;
3974 return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08003975 }
3976
Cary Clarkc5cd5e92010-11-22 15:20:02 -05003977 private int mOrientation = Configuration.ORIENTATION_UNDEFINED;
3978
3979 @Override
3980 protected void onConfigurationChanged(Configuration newConfig) {
3981 if (mSelectingText && mOrientation != newConfig.orientation) {
3982 selectionDone();
3983 }
3984 mOrientation = newConfig.orientation;
3985 }
3986
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04003987 /**
3988 * Keep track of the Callback so we can end its ActionMode or remove its
3989 * titlebar.
3990 */
3991 private SelectActionModeCallback mSelectCallback;
3992
Leon Scroggins63356742010-12-15 17:37:08 -05003993 // These values are possible options for didUpdateWebTextViewDimensions.
3994 private static final int FULLY_ON_SCREEN = 0;
3995 private static final int INTERSECTS_SCREEN = 1;
3996 private static final int ANYWHERE = 2;
3997
Leon Scrogginsc35f4ac2010-11-10 08:59:05 -05003998 /**
3999 * Check to see if the focused textfield/textarea is still on screen. If it
4000 * is, update the the dimensions and location of WebTextView. Otherwise,
4001 * remove the WebTextView. Should be called when the zoom level changes.
Leon Scroggins63356742010-12-15 17:37:08 -05004002 * @param intersection How to determine whether the textfield/textarea is
4003 * still on screen.
Leon Scrogginsc35f4ac2010-11-10 08:59:05 -05004004 * @return boolean True if the textfield/textarea is still on screen and the
4005 * dimensions/location of WebTextView have been updated.
4006 */
Leon Scroggins63356742010-12-15 17:37:08 -05004007 private boolean didUpdateWebTextViewDimensions(int intersection) {
Cary Clark5da9aeb2009-10-06 17:40:53 -04004008 Rect contentBounds = nativeFocusCandidateNodeBounds();
4009 Rect vBox = contentToViewRect(contentBounds);
4010 Rect visibleRect = new Rect();
4011 calcOurVisibleRect(visibleRect);
Leon Scrogginsbcbf5642010-02-17 17:56:51 -05004012 // If the textfield is on screen, place the WebTextView in
4013 // its new place, accounting for our new scroll/zoom values,
4014 // and adjust its textsize.
Leon Scroggins63356742010-12-15 17:37:08 -05004015 boolean onScreen;
4016 switch (intersection) {
4017 case FULLY_ON_SCREEN:
4018 onScreen = visibleRect.contains(vBox);
4019 break;
4020 case INTERSECTS_SCREEN:
4021 onScreen = Rect.intersects(visibleRect, vBox);
4022 break;
4023 case ANYWHERE:
4024 onScreen = true;
4025 break;
4026 default:
4027 throw new AssertionError(
4028 "invalid parameter passed to didUpdateWebTextViewDimensions");
4029 }
4030 if (onScreen) {
Cary Clark5da9aeb2009-10-06 17:40:53 -04004031 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
4032 vBox.height());
Leon Scrogginsc35f4ac2010-11-10 08:59:05 -05004033 mWebTextView.updateTextSize();
4034 updateWebTextViewPadding();
Cary Clark5da9aeb2009-10-06 17:40:53 -04004035 return true;
4036 } else {
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05004037 // The textfield is now off screen. The user probably
4038 // was not zooming to see the textfield better. Remove
4039 // the WebTextView. If the user types a key, and the
4040 // textfield is still in focus, we will reconstruct
4041 // the WebTextView and scroll it back on screen.
4042 mWebTextView.remove();
Cary Clark5da9aeb2009-10-06 17:40:53 -04004043 return false;
4044 }
4045 }
4046
Shimeng (Simon) Wang464b6902011-03-16 11:27:44 -07004047 void setBaseLayer(int layer, Region invalRegion, boolean showVisualIndicator,
4048 boolean isPictureAfterFirstLayout) {
Nicolas Roard67a9f2a2010-09-07 14:49:06 -07004049 if (mNativeClass == 0)
4050 return;
Shimeng (Simon) Wang464b6902011-03-16 11:27:44 -07004051 nativeSetBaseLayer(layer, invalRegion, showVisualIndicator,
4052 isPictureAfterFirstLayout);
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -08004053 if (mHTML5VideoViewProxy != null) {
4054 mHTML5VideoViewProxy.setBaseLayer(layer);
4055 }
Nicolas Roard67a9f2a2010-09-07 14:49:06 -07004056 }
4057
Derek Sollenberger293c3602010-06-04 10:44:48 -04004058 private void onZoomAnimationStart() {
4059 // If it is in password mode, turn it off so it does not draw misplaced.
4060 if (inEditingMode() && nativeFocusCandidateIsPassword()) {
4061 mWebTextView.setInPassword(false);
4062 }
4063 }
4064
4065 private void onZoomAnimationEnd() {
4066 // adjust the edit text view if needed
Leon Scroggins63356742010-12-15 17:37:08 -05004067 if (inEditingMode() && didUpdateWebTextViewDimensions(FULLY_ON_SCREEN)
Leon Scrogginsc35f4ac2010-11-10 08:59:05 -05004068 && nativeFocusCandidateIsPassword()) {
Derek Sollenberger293c3602010-06-04 10:44:48 -04004069 // If it is a password field, start drawing the WebTextView once
4070 // again.
4071 mWebTextView.setInPassword(true);
4072 }
4073 }
4074
4075 void onFixedLengthZoomAnimationStart() {
4076 WebViewCore.pauseUpdatePicture(getWebViewCore());
4077 onZoomAnimationStart();
4078 }
4079
4080 void onFixedLengthZoomAnimationEnd() {
Cary Clark0df02692010-11-24 11:01:37 -05004081 if (!mSelectingText) {
4082 WebViewCore.resumeUpdatePicture(mWebViewCore);
4083 }
Derek Sollenberger293c3602010-06-04 10:44:48 -04004084 onZoomAnimationEnd();
4085 }
4086
Grace Kloba8abd50b2010-07-08 15:02:14 -07004087 private static final int ZOOM_BITS = Paint.FILTER_BITMAP_FLAG |
4088 Paint.DITHER_FLAG |
4089 Paint.SUBPIXEL_TEXT_FLAG;
4090 private static final int SCROLL_BITS = Paint.FILTER_BITMAP_FLAG |
4091 Paint.DITHER_FLAG;
4092
4093 private final DrawFilter mZoomFilter =
4094 new PaintFlagsDrawFilter(ZOOM_BITS, Paint.LINEAR_TEXT_FLAG);
4095 // If we need to trade better quality for speed, set mScrollFilter to null
4096 private final DrawFilter mScrollFilter =
4097 new PaintFlagsDrawFilter(SCROLL_BITS, 0);
4098
Cary Clarkd6982c92009-05-29 11:02:22 -04004099 private void drawCoreAndCursorRing(Canvas canvas, int color,
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004100 boolean drawCursorRing) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004101 if (mDrawHistory) {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04004102 canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004103 canvas.drawPicture(mHistoryPicture);
4104 return;
4105 }
Grace Kloba8abd50b2010-07-08 15:02:14 -07004106 if (mNativeClass == 0) return;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004107
Derek Sollenberger293c3602010-06-04 10:44:48 -04004108 boolean animateZoom = mZoomManager.isFixedLengthAnimationInProgress();
Grace Klobac2242f22010-03-05 14:00:26 -08004109 boolean animateScroll = ((!mScroller.isFinished()
Cary Clark25415e22009-10-12 13:41:28 -04004110 || mVelocityTracker != null)
4111 && (mTouchMode != TOUCH_DRAG_MODE ||
Grace Klobac2242f22010-03-05 14:00:26 -08004112 mHeldMotionless != MOTIONLESS_TRUE))
4113 || mDeferTouchMode == TOUCH_DRAG_MODE;
Cary Clark25415e22009-10-12 13:41:28 -04004114 if (mTouchMode == TOUCH_DRAG_MODE) {
4115 if (mHeldMotionless == MOTIONLESS_PENDING) {
4116 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
4117 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
4118 mHeldMotionless = MOTIONLESS_FALSE;
4119 }
4120 if (mHeldMotionless == MOTIONLESS_FALSE) {
4121 mPrivateHandler.sendMessageDelayed(mPrivateHandler
4122 .obtainMessage(DRAG_HELD_MOTIONLESS), MOTIONLESS_TIME);
4123 mHeldMotionless = MOTIONLESS_PENDING;
4124 }
4125 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004126 if (animateZoom) {
Derek Sollenberger293c3602010-06-04 10:44:48 -04004127 mZoomManager.animateZoom(canvas);
Romain Guyd6cf4772011-03-04 17:10:54 -08004128 } else if (!canvas.isHardwareAccelerated()) {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04004129 canvas.scale(mZoomManager.getScale(), mZoomManager.getScale());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004130 }
4131
Nicolas Roardcc2c8422010-04-19 19:08:55 -07004132 boolean UIAnimationsRunning = false;
4133 // Currently for each draw we compute the animation values;
4134 // We may in the future decide to do that independently.
4135 if (mNativeClass != 0 && nativeEvaluateLayersAnimations()) {
4136 UIAnimationsRunning = true;
4137 // If we have unfinished (or unstarted) animations,
Nicolas Roardf2674c12011-03-16 16:04:57 -07004138 // we ask for a repaint. We only need to do this in software
4139 // rendering (with hardware rendering we already have a different
4140 // method of requesting a repaint)
4141 if (!canvas.isHardwareAccelerated())
4142 invalidate();
Nicolas Roardcc2c8422010-04-19 19:08:55 -07004143 }
Grace Kloba8abd50b2010-07-08 15:02:14 -07004144
Cary Clark2ec30692010-02-23 10:50:38 -05004145 // decide which adornments to draw
4146 int extras = DRAW_EXTRAS_NONE;
4147 if (mFindIsUp) {
Cary Clark924af702010-06-04 16:37:43 -04004148 extras = DRAW_EXTRAS_FIND;
4149 } else if (mSelectingText) {
4150 extras = DRAW_EXTRAS_SELECTION;
4151 nativeSetSelectionPointer(mDrawSelectionPointer,
4152 mZoomManager.getInvScale(),
4153 mSelectX, mSelectY - getTitleHeight());
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004154 } else if (drawCursorRing) {
Cary Clark2ec30692010-02-23 10:50:38 -05004155 extras = DRAW_EXTRAS_CURSOR_RING;
4156 }
Cary Clark6f5dfc62010-11-11 13:09:20 -05004157 if (DebugFlags.WEB_VIEW) {
4158 Log.v(LOGTAG, "mFindIsUp=" + mFindIsUp
4159 + " mSelectingText=" + mSelectingText
4160 + " nativePageShouldHandleShiftAndArrows()="
4161 + nativePageShouldHandleShiftAndArrows()
4162 + " animateZoom=" + animateZoom
4163 + " extras=" + extras);
4164 }
Nicolas Roard67a9f2a2010-09-07 14:49:06 -07004165
4166 if (canvas.isHardwareAccelerated()) {
Chet Haase91fc3cf2011-01-28 00:20:04 -08004167 int functor = nativeGetDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
Romain Guycabfcc12011-03-07 18:06:46 -08004168 mGLViewportEmpty ? null : mViewRectViewport, getScale(), extras);
Chet Haasedaf98e92011-01-10 14:10:36 -08004169 ((HardwareCanvas) canvas).callDrawGLFunction(functor);
Nicolas Roard67a9f2a2010-09-07 14:49:06 -07004170 } else {
4171 DrawFilter df = null;
4172 if (mZoomManager.isZoomAnimating() || UIAnimationsRunning) {
4173 df = mZoomFilter;
4174 } else if (animateScroll) {
4175 df = mScrollFilter;
4176 }
4177 canvas.setDrawFilter(df);
Patrick Scott85b69e02011-01-25 15:19:45 -05004178 // XXX: Revisit splitting content. Right now it causes a
4179 // synchronization problem with layers.
4180 int content = nativeDraw(canvas, color, extras, false);
Nicolas Roard67a9f2a2010-09-07 14:49:06 -07004181 canvas.setDrawFilter(null);
4182 if (content != 0) {
4183 mWebViewCore.sendMessage(EventHub.SPLIT_PICTURE_SET, content, 0);
4184 }
Grace Kloba8abd50b2010-07-08 15:02:14 -07004185 }
Cary Clark2ec30692010-02-23 10:50:38 -05004186
4187 if (extras == DRAW_EXTRAS_CURSOR_RING) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004188 if (mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
4189 mTouchMode = TOUCH_SHORTPRESS_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004191 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04004192 if (mFocusSizeChanged) {
4193 mFocusSizeChanged = false;
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05004194 // If we are zooming, this will get handled above, when the zoom
4195 // finishes. We also do not need to do this unless the WebTextView
4196 // is showing.
4197 if (!animateZoom && inEditingMode()) {
Leon Scroggins63356742010-12-15 17:37:08 -05004198 didUpdateWebTextViewDimensions(ANYWHERE);
Leon Scrogginsecfc0eb2009-11-19 13:47:46 -05004199 }
Cary Clark5da9aeb2009-10-06 17:40:53 -04004200 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004201 }
4202
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004203 // draw history
4204 private boolean mDrawHistory = false;
4205 private Picture mHistoryPicture = null;
4206 private int mHistoryWidth = 0;
4207 private int mHistoryHeight = 0;
4208
4209 // Only check the flag, can be called from WebCore thread
4210 boolean drawHistory() {
4211 return mDrawHistory;
4212 }
4213
Derek Sollenberger341e22f2010-06-02 12:34:34 -04004214 int getHistoryPictureWidth() {
4215 return (mHistoryPicture != null) ? mHistoryPicture.getWidth() : 0;
4216 }
4217
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004218 // Should only be called in UI thread
4219 void switchOutDrawHistory() {
4220 if (null == mWebViewCore) return; // CallbackProxy may trigger this
Grace Kloba8abd50b2010-07-08 15:02:14 -07004221 if (mDrawHistory && (getProgress() == 100 || nativeHasContent())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004222 mDrawHistory = false;
Patrick Scottda9a22b2010-04-08 08:32:52 -04004223 mHistoryPicture = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004224 invalidate();
4225 int oldScrollX = mScrollX;
4226 int oldScrollY = mScrollY;
4227 mScrollX = pinLocX(mScrollX);
4228 mScrollY = pinLocY(mScrollY);
4229 if (oldScrollX != mScrollX || oldScrollY != mScrollY) {
Grace Kloba327dce92010-04-02 15:38:55 -07004230 onScrollChanged(mScrollX, mScrollY, oldScrollX, oldScrollY);
4231 } else {
4232 sendOurVisibleRect();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004233 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004234 }
4235 }
4236
Cary Clarkd6982c92009-05-29 11:02:22 -04004237 WebViewCore.CursorData cursorData() {
4238 WebViewCore.CursorData result = new WebViewCore.CursorData();
4239 result.mMoveGeneration = nativeMoveGeneration();
4240 result.mFrame = nativeCursorFramePointer();
Cary Clarked56eda2009-06-18 09:48:47 -04004241 Point position = nativeCursorPosition();
4242 result.mX = position.x;
4243 result.mY = position.y;
Cary Clarkd6982c92009-05-29 11:02:22 -04004244 return result;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004245 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004247 /**
4248 * Delete text from start to end in the focused textfield. If there is no
Cary Clarkd6982c92009-05-29 11:02:22 -04004249 * focus, or if start == end, silently fail. If start and end are out of
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004250 * order, swap them.
4251 * @param start Beginning of selection to delete.
4252 * @param end End of selection to delete.
4253 */
4254 /* package */ void deleteSelection(int start, int end) {
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07004255 mTextGeneration++;
Leon Scroggins6679f2f2009-08-12 18:48:10 -04004256 WebViewCore.TextSelectionData data
4257 = new WebViewCore.TextSelectionData(start, end);
4258 mWebViewCore.sendMessage(EventHub.DELETE_SELECTION, mTextGeneration, 0,
4259 data);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004260 }
4261
4262 /**
4263 * Set the selection to (start, end) in the focused textfield. If start and
4264 * end are out of order, swap them.
4265 * @param start Beginning of selection.
4266 * @param end End of selection.
4267 */
4268 /* package */ void setSelection(int start, int end) {
Leon Scrogginsacea08d2010-05-27 15:09:32 -04004269 if (mWebViewCore != null) {
4270 mWebViewCore.sendMessage(EventHub.SET_SELECTION, start, end);
4271 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004272 }
4273
Derek Sollenberger7cabb032010-01-21 10:37:38 -05004274 @Override
4275 public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
4276 InputConnection connection = super.onCreateInputConnection(outAttrs);
4277 outAttrs.imeOptions |= EditorInfo.IME_FLAG_NO_FULLSCREEN;
4278 return connection;
4279 }
4280
Leon Scroggins04e0a102010-01-08 16:19:27 -05004281 /**
4282 * Called in response to a message from webkit telling us that the soft
4283 * keyboard should be launched.
4284 */
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04004285 private void displaySoftKeyboard(boolean isTextView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004286 InputMethodManager imm = (InputMethodManager)
4287 getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04004288
Shimeng (Simon) Wang7924a4a2010-09-14 13:20:46 -07004289 // bring it back to the default level scale so that user can enter text
Shimeng (Simon) Wang6c09ef02010-09-09 13:56:34 -07004290 boolean zoom = mZoomManager.getScale() < mZoomManager.getDefaultScale();
Leon Scrogginse3844ee2010-02-23 15:15:16 -05004291 if (zoom) {
Derek Sollenberger03e48912010-05-18 17:03:42 -04004292 mZoomManager.setZoomCenter(mLastTouchX, mLastTouchY);
Shimeng (Simon) Wang6c09ef02010-09-09 13:56:34 -07004293 mZoomManager.setZoomScale(mZoomManager.getDefaultScale(), false);
Leon Scrogginse3844ee2010-02-23 15:15:16 -05004294 }
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04004295 if (isTextView) {
Leon Scroggins04e0a102010-01-08 16:19:27 -05004296 rebuildWebTextView();
Leon Scrogginse3844ee2010-02-23 15:15:16 -05004297 if (inEditingMode()) {
Leon Scrogginsd69b7012011-03-09 16:18:28 -05004298 imm.showSoftInput(mWebTextView, 0, mWebTextView.getResultReceiver());
Leon Scrogginse3844ee2010-02-23 15:15:16 -05004299 if (zoom) {
Leon Scroggins63356742010-12-15 17:37:08 -05004300 didUpdateWebTextViewDimensions(INTERSECTS_SCREEN);
Leon Scrogginse3844ee2010-02-23 15:15:16 -05004301 }
4302 return;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07004303 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004304 }
Leon Scroggins200c13d2010-05-14 15:35:42 -04004305 // Used by plugins and contentEditable.
Leon Scrogginse3844ee2010-02-23 15:15:16 -05004306 // Also used if the navigation cache is out of date, and
4307 // does not recognize that a textfield is in focus. In that
4308 // case, use WebView as the targeted view.
4309 // see http://b/issue?id=2457459
4310 imm.showSoftInput(this, 0);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04004311 }
4312
4313 // Called by WebKit to instruct the UI to hide the keyboard
4314 private void hideSoftKeyboard() {
Leon Scrogginsf2e17a82010-09-24 15:58:50 -04004315 InputMethodManager imm = InputMethodManager.peekInstance();
4316 if (imm != null && (imm.isActive(this)
4317 || (inEditingMode() && imm.isActive(mWebTextView)))) {
Leon Scroggins III71d17e42010-09-02 12:53:08 -04004318 imm.hideSoftInputFromWindow(this.getWindowToken(), 0);
4319 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004320 }
4321
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004322 /*
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004323 * This method checks the current focus and cursor and potentially rebuilds
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004324 * mWebTextView to have the appropriate properties, such as password,
4325 * multiline, and what text it contains. It also removes it if necessary.
4326 */
Leon Scroggins01058282009-07-30 16:33:56 -04004327 /* package */ void rebuildWebTextView() {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004328 // If the WebView does not have focus, do nothing until it gains focus.
Grace Kloba04b28682009-09-14 14:38:37 -07004329 if (!hasFocus() && (null == mWebTextView || !mWebTextView.hasFocus())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004330 return;
4331 }
4332 boolean alreadyThere = inEditingMode();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004333 // inEditingMode can only return true if mWebTextView is non-null,
Leon Scroggins2ca912e2009-04-28 16:51:55 -04004334 // so we can safely call remove() if (alreadyThere)
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004335 if (0 == mNativeClass || !nativeFocusCandidateIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004336 if (alreadyThere) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004337 mWebTextView.remove();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004338 }
4339 return;
4340 }
Leon Scroggins2ca912e2009-04-28 16:51:55 -04004341 // At this point, we know we have found an input field, so go ahead
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004342 // and create the WebTextView if necessary.
4343 if (mWebTextView == null) {
Ben Murdochdb8d19c2010-10-29 11:44:17 +01004344 mWebTextView = new WebTextView(mContext, WebView.this, mAutoFillData.getQueryId());
Leon Scroggins2ca912e2009-04-28 16:51:55 -04004345 // Initialize our generation number.
4346 mTextGeneration = 0;
4347 }
Leon Scrogginsc35f4ac2010-11-10 08:59:05 -05004348 mWebTextView.updateTextSize();
Cary Clark3524be92009-06-22 13:09:11 -04004349 Rect visibleRect = new Rect();
4350 calcOurContentVisibleRect(visibleRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004351 // Note that sendOurVisibleRect calls viewToContent, so the coordinates
4352 // should be in content coordinates.
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004353 Rect bounds = nativeFocusCandidateNodeBounds();
Leon Scroggins6be3bf22009-12-08 13:43:47 -05004354 Rect vBox = contentToViewRect(bounds);
4355 mWebTextView.setRect(vBox.left, vBox.top, vBox.width(), vBox.height());
Cary Clarkd6982c92009-05-29 11:02:22 -04004356 if (!Rect.intersects(bounds, visibleRect)) {
Leon Scroggins3be1ffa2011-01-19 12:49:57 -05004357 revealSelection();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004358 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004359 String text = nativeFocusCandidateText();
4360 int nodePointer = nativeFocusCandidatePointer();
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004361 if (alreadyThere && mWebTextView.isSameTextField(nodePointer)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004362 // It is possible that we have the same textfield, but it has moved,
4363 // i.e. In the case of opening/closing the screen.
4364 // In that case, we need to set the dimensions, but not the other
4365 // aspects.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004366 // If the text has been changed by webkit, update it. However, if
4367 // there has been more UI text input, ignore it. We will receive
4368 // another update when that text is recognized.
Leon Scroggins6be3bf22009-12-08 13:43:47 -05004369 if (text != null && !text.equals(mWebTextView.getText().toString())
Cary Clarkd6982c92009-05-29 11:02:22 -04004370 && nativeTextGeneration() == mTextGeneration) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004371 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004372 }
4373 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004374 mWebTextView.setGravity(nativeFocusCandidateIsRtlText() ?
4375 Gravity.RIGHT : Gravity.NO_GRAVITY);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05004376 // This needs to be called before setType, which may call
4377 // requestFormData, and it needs to have the correct nodePointer.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004378 mWebTextView.setNodePointer(nodePointer);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05004379 mWebTextView.setType(nativeFocusCandidateType());
Leon Scrogginsc35f4ac2010-11-10 08:59:05 -05004380 updateWebTextViewPadding();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004381 if (null == text) {
Cary Clark243ea062009-06-25 10:49:32 -04004382 if (DebugFlags.WEB_VIEW) {
4383 Log.v(LOGTAG, "rebuildWebTextView null == text");
4384 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04004385 text = "";
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004386 }
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04004387 mWebTextView.setTextAndKeepSelection(text);
Leon Scroggins5c84bf02010-02-09 17:03:44 -05004388 InputMethodManager imm = InputMethodManager.peekInstance();
4389 if (imm != null && imm.isActive(mWebTextView)) {
4390 imm.restartInput(mWebTextView);
4391 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004392 }
Leon Scrogginsf8ca2d72010-10-21 17:14:42 -04004393 if (isFocused()) {
4394 mWebTextView.requestFocus();
4395 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004396 }
4397
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05004398 /**
Leon Scrogginsc35f4ac2010-11-10 08:59:05 -05004399 * Update the padding of mWebTextView based on the native textfield/textarea
4400 */
4401 void updateWebTextViewPadding() {
4402 Rect paddingRect = nativeFocusCandidatePaddingRect();
4403 if (paddingRect != null) {
4404 // Use contentToViewDimension since these are the dimensions of
4405 // the padding.
4406 mWebTextView.setPadding(
4407 contentToViewDimension(paddingRect.left),
4408 contentToViewDimension(paddingRect.top),
4409 contentToViewDimension(paddingRect.right),
4410 contentToViewDimension(paddingRect.bottom));
4411 }
4412 }
4413
4414 /**
Leon Scroggins200c13d2010-05-14 15:35:42 -04004415 * Tell webkit to put the cursor on screen.
4416 */
4417 /* package */ void revealSelection() {
4418 if (mWebViewCore != null) {
4419 mWebViewCore.sendMessage(EventHub.REVEAL_SELECTION);
4420 }
4421 }
4422
4423 /**
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05004424 * Called by WebTextView to find saved form data associated with the
4425 * textfield
4426 * @param name Name of the textfield.
4427 * @param nodePointer Pointer to the node of the textfield, so it can be
4428 * compared to the currently focused textfield when the data is
4429 * retrieved.
Ben Murdoch62275a42010-09-07 11:27:28 +01004430 * @param autoFillable true if WebKit has determined this field is part of
4431 * a form that can be auto filled.
Leon Scrogginsae0238c2011-01-05 15:12:55 -05004432 * @param autoComplete true if the attribute "autocomplete" is set to true
4433 * on the textfield.
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05004434 */
Leon Scrogginsae0238c2011-01-05 15:12:55 -05004435 /* package */ void requestFormData(String name, int nodePointer,
4436 boolean autoFillable, boolean autoComplete) {
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05004437 if (mWebViewCore.getSettings().getSaveFormData()) {
4438 Message update = mPrivateHandler.obtainMessage(REQUEST_FORM_DATA);
4439 update.arg1 = nodePointer;
4440 RequestFormData updater = new RequestFormData(name, getUrl(),
Leon Scrogginsae0238c2011-01-05 15:12:55 -05004441 update, autoFillable, autoComplete);
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05004442 Thread t = new Thread(updater);
4443 t.start();
4444 }
4445 }
4446
Leon Scroggins3a503392010-01-06 17:04:38 -05004447 /**
4448 * Pass a message to find out the <label> associated with the <input>
4449 * identified by nodePointer
4450 * @param framePointer Pointer to the frame containing the <input> node
4451 * @param nodePointer Pointer to the node for which a <label> is desired.
4452 */
4453 /* package */ void requestLabel(int framePointer, int nodePointer) {
4454 mWebViewCore.sendMessage(EventHub.REQUEST_LABEL, framePointer,
4455 nodePointer);
4456 }
4457
Leon Scrogginsd3465f62009-06-02 10:57:54 -04004458 /*
4459 * This class requests an Adapter for the WebTextView which shows past
4460 * entries stored in the database. It is a Runnable so that it can be done
4461 * in its own thread, without slowing down the UI.
4462 */
4463 private class RequestFormData implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004464 private String mName;
4465 private String mUrl;
4466 private Message mUpdateMessage;
Ben Murdoch62275a42010-09-07 11:27:28 +01004467 private boolean mAutoFillable;
Leon Scrogginsae0238c2011-01-05 15:12:55 -05004468 private boolean mAutoComplete;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004469
Leon Scrogginsae0238c2011-01-05 15:12:55 -05004470 public RequestFormData(String name, String url, Message msg,
4471 boolean autoFillable, boolean autoComplete) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004472 mName = name;
4473 mUrl = url;
4474 mUpdateMessage = msg;
Ben Murdoch62275a42010-09-07 11:27:28 +01004475 mAutoFillable = autoFillable;
Leon Scrogginsae0238c2011-01-05 15:12:55 -05004476 mAutoComplete = autoComplete;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004477 }
4478
4479 public void run() {
Leon Scrogginsae0238c2011-01-05 15:12:55 -05004480 ArrayList<String> pastEntries = new ArrayList<String>();
Ben Murdoch62275a42010-09-07 11:27:28 +01004481
4482 if (mAutoFillable) {
4483 // Note that code inside the adapter click handler in WebTextView depends
4484 // on the AutoFill item being at the top of the drop down list. If you change
4485 // the order, make sure to do it there too!
Ben Murdoch57914382010-11-16 11:50:39 +00004486 WebSettings settings = getSettings();
4487 if (settings != null && settings.getAutoFillProfile() != null) {
4488 pastEntries.add(getResources().getText(
4489 com.android.internal.R.string.autofill_this_form).toString() +
4490 " " +
4491 mAutoFillData.getPreviewString());
4492 mWebTextView.setAutoFillProfileIsSet(true);
4493 } else {
4494 // There is no autofill profile set up yet, so add an option that
4495 // will invite the user to set their profile up.
4496 pastEntries.add(getResources().getText(
4497 com.android.internal.R.string.setup_autofill).toString());
4498 mWebTextView.setAutoFillProfileIsSet(false);
4499 }
Ben Murdoch62275a42010-09-07 11:27:28 +01004500 }
4501
Leon Scrogginsae0238c2011-01-05 15:12:55 -05004502 if (mAutoComplete) {
4503 pastEntries.addAll(mDatabase.getFormData(mUrl, mName));
4504 }
Ben Murdoch62275a42010-09-07 11:27:28 +01004505
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004506 if (pastEntries.size() > 0) {
4507 AutoCompleteAdapter adapter = new
4508 AutoCompleteAdapter(mContext, pastEntries);
Cary Clarkded054c2009-06-15 10:26:08 -04004509 mUpdateMessage.obj = adapter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004510 mUpdateMessage.sendToTarget();
4511 }
4512 }
4513 }
4514
Grace Kloba8ae1b412009-11-23 10:35:34 -08004515 /**
4516 * Dump the display tree to "/sdcard/displayTree.txt"
4517 *
4518 * @hide debug only
4519 */
4520 public void dumpDisplayTree() {
4521 nativeDumpDisplayTree(getUrl());
4522 }
4523
4524 /**
4525 * Dump the dom tree to adb shell if "toFile" is False, otherwise dump it to
4526 * "/sdcard/domTree.txt"
4527 *
4528 * @hide debug only
4529 */
4530 public void dumpDomTree(boolean toFile) {
4531 mWebViewCore.sendMessage(EventHub.DUMP_DOMTREE, toFile ? 1 : 0, 0);
4532 }
4533
4534 /**
4535 * Dump the render tree to adb shell if "toFile" is False, otherwise dump it
4536 * to "/sdcard/renderTree.txt"
4537 *
4538 * @hide debug only
4539 */
4540 public void dumpRenderTree(boolean toFile) {
4541 mWebViewCore.sendMessage(EventHub.DUMP_RENDERTREE, toFile ? 1 : 0, 0);
4542 }
4543
Andrei Popescu5e7bb0a2010-02-01 22:32:16 +00004544 /**
Steve Block68dede32010-08-04 10:28:46 +01004545 * Called by DRT on UI thread, need to proxy to WebCore thread.
4546 *
4547 * @hide debug only
4548 */
4549 public void useMockDeviceOrientation() {
4550 mWebViewCore.sendMessage(EventHub.USE_MOCK_DEVICE_ORIENTATION);
4551 }
4552
4553 /**
4554 * Called by DRT on WebCore thread.
4555 *
4556 * @hide debug only
4557 */
4558 public void setMockDeviceOrientation(boolean canProvideAlpha, double alpha,
4559 boolean canProvideBeta, double beta, boolean canProvideGamma, double gamma) {
Steve Blockf4a705f2010-08-11 13:08:24 +01004560 mWebViewCore.setMockDeviceOrientation(canProvideAlpha, alpha, canProvideBeta, beta,
4561 canProvideGamma, gamma);
Steve Block68dede32010-08-04 10:28:46 +01004562 }
4563
4564 /**
Andrei Popescu5e7bb0a2010-02-01 22:32:16 +00004565 * Dump the V8 counters to standard output.
4566 * Note that you need a build with V8 and WEBCORE_INSTRUMENTATION set to
4567 * true. Otherwise, this will do nothing.
4568 *
4569 * @hide debug only
4570 */
4571 public void dumpV8Counters() {
4572 mWebViewCore.sendMessage(EventHub.DUMP_V8COUNTERS);
4573 }
4574
Leon Scrogginse3225672009-06-03 15:53:13 -04004575 // This is used to determine long press with the center key. Does not
4576 // affect long press with the trackball/touch.
4577 private boolean mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004578
4579 @Override
Derek Sollenbergerff0f9732010-08-06 11:50:49 -04004580 public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
4581 // send complex characters to webkit for use by JS and plugins
4582 if (keyCode == KeyEvent.KEYCODE_UNKNOWN && event.getCharacters() != null) {
4583 // pass the key to DOM
4584 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
4585 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
4586 // return true as DOM handles the key
4587 return true;
4588 }
4589 return false;
4590 }
4591
Cary Clarkaa86ac82010-12-28 11:30:18 -05004592 private boolean isEnterActionKey(int keyCode) {
4593 return keyCode == KeyEvent.KEYCODE_DPAD_CENTER
4594 || keyCode == KeyEvent.KEYCODE_ENTER
4595 || keyCode == KeyEvent.KEYCODE_NUMPAD_ENTER;
4596 }
4597
Derek Sollenbergerff0f9732010-08-06 11:50:49 -04004598 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004599 public boolean onKeyDown(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004600 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004601 Log.v(LOGTAG, "keyDown at " + System.currentTimeMillis()
Cary Clarkaa86ac82010-12-28 11:30:18 -05004602 + "keyCode=" + keyCode
Cary Clark215b72c2009-06-26 14:38:43 -04004603 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004604 }
4605
Cary Clark122da932010-12-28 15:58:08 -05004606 // don't implement accelerator keys here; defer to host application
4607 if (event.isCtrlPressed()) {
4608 return false;
4609 }
4610
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004611 if (mNativeClass == 0) {
4612 return false;
4613 }
4614
4615 // do this hack up front, so it always works, regardless of touch-mode
4616 if (AUTO_REDRAW_HACK && (keyCode == KeyEvent.KEYCODE_CALL)) {
4617 mAutoRedraw = !mAutoRedraw;
4618 if (mAutoRedraw) {
4619 invalidate();
4620 }
4621 return true;
4622 }
4623
4624 // Bubble up the key event if
4625 // 1. it is a system key; or
Grace Kloba04b28682009-09-14 14:38:37 -07004626 // 2. the host application wants to handle it;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004627 if (event.isSystem()
Svetoslav Ganovc93fb652011-01-05 18:52:05 -08004628 || mCallbackProxy.uiOverrideKeyEvent(event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004629 return false;
4630 }
4631
Svetoslav Ganovc93fb652011-01-05 18:52:05 -08004632 // accessibility support
Svetoslav Ganov12bed782011-01-03 14:14:50 -08004633 if (accessibilityScriptInjected()) {
Svetoslav Ganovb01c3d22011-01-11 14:19:17 -08004634 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
4635 // if an accessibility script is injected we delegate to it the key handling.
4636 // this script is a screen reader which is a fully fledged solution for blind
4637 // users to navigate in and interact with web pages.
4638 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
4639 return true;
4640 } else {
4641 // Clean up if accessibility was disabled after loading the current URL.
4642 mAccessibilityScriptInjected = false;
4643 }
4644 } else if (mAccessibilityInjector != null) {
4645 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
4646 if (mAccessibilityInjector.onKeyEvent(event)) {
4647 // if an accessibility injector is present (no JavaScript enabled or the site
4648 // opts out injecting our JavaScript screen reader) we let it decide whether
4649 // to act on and consume the event.
4650 return true;
4651 }
4652 } else {
4653 // Clean up if accessibility was disabled after loading the current URL.
4654 mAccessibilityInjector = null;
4655 }
Svetoslav Ganov12bed782011-01-03 14:14:50 -08004656 }
4657
Chih-Wei Huang4e916ad2010-06-10 09:56:47 +08004658 if (keyCode == KeyEvent.KEYCODE_PAGE_UP) {
Cary Clarka3ee56f2011-01-10 11:20:56 -05004659 if (event.hasNoModifiers()) {
4660 pageUp(false);
4661 return true;
4662 } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
4663 pageUp(true);
4664 return true;
4665 }
Chih-Wei Huang4e916ad2010-06-10 09:56:47 +08004666 }
4667
4668 if (keyCode == KeyEvent.KEYCODE_PAGE_DOWN) {
Cary Clarka3ee56f2011-01-10 11:20:56 -05004669 if (event.hasNoModifiers()) {
4670 pageDown(false);
4671 return true;
4672 } else if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
4673 pageDown(true);
4674 return true;
4675 }
4676 }
4677
4678 if (keyCode == KeyEvent.KEYCODE_MOVE_HOME && event.hasNoModifiers()) {
4679 pageUp(true);
4680 return true;
4681 }
4682
4683 if (keyCode == KeyEvent.KEYCODE_MOVE_END && event.hasNoModifiers()) {
4684 pageDown(true);
Chih-Wei Huang4e916ad2010-06-10 09:56:47 +08004685 return true;
4686 }
4687
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004688 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
4689 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004690 switchOutDrawHistory();
Svetoslav Ganov12bed782011-01-03 14:14:50 -08004691 if (nativePageShouldHandleShiftAndArrows()) {
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07004692 letPageHandleNavKey(keyCode, event.getEventTime(), true, event.getMetaState());
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05004693 return true;
4694 }
Cary Clarka3ee56f2011-01-10 11:20:56 -05004695 if (event.hasModifiers(KeyEvent.META_ALT_ON)) {
4696 switch (keyCode) {
4697 case KeyEvent.KEYCODE_DPAD_UP:
4698 pageUp(true);
4699 return true;
4700 case KeyEvent.KEYCODE_DPAD_DOWN:
4701 pageDown(true);
4702 return true;
4703 case KeyEvent.KEYCODE_DPAD_LEFT:
4704 nativeClearCursor(); // start next trackball movement from page edge
4705 return pinScrollTo(0, mScrollY, true, 0);
4706 case KeyEvent.KEYCODE_DPAD_RIGHT:
4707 nativeClearCursor(); // start next trackball movement from page edge
4708 return pinScrollTo(mContentWidth, mScrollY, true, 0);
4709 }
4710 }
Cary Clark924af702010-06-04 16:37:43 -04004711 if (mSelectingText) {
Cary Clarkc05af372009-10-16 10:52:27 -04004712 int xRate = keyCode == KeyEvent.KEYCODE_DPAD_LEFT
4713 ? -1 : keyCode == KeyEvent.KEYCODE_DPAD_RIGHT ? 1 : 0;
4714 int yRate = keyCode == KeyEvent.KEYCODE_DPAD_UP ?
4715 -1 : keyCode == KeyEvent.KEYCODE_DPAD_DOWN ? 1 : 0;
4716 int multiplier = event.getRepeatCount() + 1;
4717 moveSelection(xRate * multiplier, yRate * multiplier);
4718 return true;
4719 }
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05004720 if (navHandledKey(keyCode, 1, false, event.getEventTime())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004721 playSoundEffect(keyCodeToSoundsEffect(keyCode));
4722 return true;
4723 }
4724 // Bubble up the key event as WebView doesn't handle it
4725 return false;
4726 }
4727
Cary Clark1477b8f2011-02-09 19:33:00 -05004728 if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004729 switchOutDrawHistory();
Cary Clarkddbda002011-01-06 13:40:15 -05004730 boolean wantsKeyEvents = nativeCursorNodePointer() == 0
4731 || nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004732 if (event.getRepeatCount() == 0) {
Cary Clark924af702010-06-04 16:37:43 -04004733 if (mSelectingText) {
Cary Clarkc05af372009-10-16 10:52:27 -04004734 return true; // discard press if copy in progress
4735 }
Leon Scrogginse3225672009-06-03 15:53:13 -04004736 mGotCenterDown = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004737 mPrivateHandler.sendMessageDelayed(mPrivateHandler
Leon Scrogginse3225672009-06-03 15:53:13 -04004738 .obtainMessage(LONG_PRESS_CENTER), LONG_PRESS_TIMEOUT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004739 // Already checked mNativeClass, so we do not need to check it
4740 // again.
4741 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Cary Clarkddbda002011-01-06 13:40:15 -05004742 if (!wantsKeyEvents) return true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004743 }
4744 // Bubble up the key event as WebView doesn't handle it
Cary Clarkddbda002011-01-06 13:40:15 -05004745 if (!wantsKeyEvents) return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004746 }
4747
4748 if (getSettings().getNavDump()) {
4749 switch (keyCode) {
4750 case KeyEvent.KEYCODE_4:
Grace Kloba8ae1b412009-11-23 10:35:34 -08004751 dumpDisplayTree();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004752 break;
4753 case KeyEvent.KEYCODE_5:
4754 case KeyEvent.KEYCODE_6:
Grace Kloba8ae1b412009-11-23 10:35:34 -08004755 dumpDomTree(keyCode == KeyEvent.KEYCODE_5);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004756 break;
4757 case KeyEvent.KEYCODE_7:
4758 case KeyEvent.KEYCODE_8:
Grace Kloba8ae1b412009-11-23 10:35:34 -08004759 dumpRenderTree(keyCode == KeyEvent.KEYCODE_7);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004760 break;
4761 case KeyEvent.KEYCODE_9:
4762 nativeInstrumentReport();
4763 return true;
4764 }
4765 }
4766
Derek Sollenberger718d69f2009-10-19 15:56:43 -04004767 if (nativeCursorIsTextInput()) {
Leon Scroggins1cc24202009-06-16 10:10:28 -04004768 // This message will put the node in focus, for the DOM's notion
Leon Scrogginsb45a2632011-01-12 14:11:24 -05004769 // of focus.
4770 mWebViewCore.sendMessage(EventHub.FAKE_CLICK, nativeCursorFramePointer(),
Leon Scroggins01058282009-07-30 16:33:56 -04004771 nativeCursorNodePointer());
Leon Scroggins0658e8f2009-06-26 14:09:09 -04004772 // This will bring up the WebTextView and put it in focus, for
4773 // our view system's notion of focus
4774 rebuildWebTextView();
4775 // Now we need to pass the event to it
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04004776 if (inEditingMode()) {
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04004777 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04004778 return mWebTextView.dispatchKeyEvent(event);
Leon Scrogginsc66f68a2009-10-02 15:12:30 -04004779 }
Leon Scroggins40981262009-07-01 10:57:47 -04004780 } else if (nativeHasFocusNode()) {
4781 // In this case, the cursor is not on a text input, but the focus
4782 // might be. Check it, and if so, hand over to the WebTextView.
4783 rebuildWebTextView();
4784 if (inEditingMode()) {
Leon Scroggins0aa341f2010-02-24 16:49:35 -05004785 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04004786 return mWebTextView.dispatchKeyEvent(event);
Leon Scroggins40981262009-07-01 10:57:47 -04004787 }
Cary Clark19436562009-06-04 16:25:07 -04004788 }
4789
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004790 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04004791 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004792 // pass the key to DOM
4793 mWebViewCore.sendMessage(EventHub.KEY_DOWN, event);
4794 // return true as DOM handles the key
4795 return true;
4796 }
4797
4798 // Bubble up the key event as WebView doesn't handle it
4799 return false;
4800 }
4801
4802 @Override
4803 public boolean onKeyUp(int keyCode, KeyEvent event) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04004804 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004805 Log.v(LOGTAG, "keyUp at " + System.currentTimeMillis()
Cary Clark215b72c2009-06-26 14:38:43 -04004806 + ", " + event + ", unicode=" + event.getUnicodeChar());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004807 }
4808
4809 if (mNativeClass == 0) {
4810 return false;
4811 }
4812
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04004813 // special CALL handling when cursor node's href is "tel:XXX"
Cary Clarkd6982c92009-05-29 11:02:22 -04004814 if (keyCode == KeyEvent.KEYCODE_CALL && nativeHasCursorNode()) {
4815 String text = nativeCursorText();
4816 if (!nativeCursorIsTextInput() && text != null
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004817 && text.startsWith(SCHEME_TEL)) {
4818 Intent intent = new Intent(Intent.ACTION_DIAL, Uri.parse(text));
4819 getContext().startActivity(intent);
4820 return true;
4821 }
4822 }
4823
4824 // Bubble up the key event if
4825 // 1. it is a system key; or
4826 // 2. the host application wants to handle it;
Svetoslav Ganovda355512010-05-12 22:04:44 -07004827 if (event.isSystem()
Svetoslav Ganovc93fb652011-01-05 18:52:05 -08004828 || mCallbackProxy.uiOverrideKeyEvent(event)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004829 return false;
4830 }
4831
Svetoslav Ganovc93fb652011-01-05 18:52:05 -08004832 // accessibility support
Svetoslav Ganov12bed782011-01-03 14:14:50 -08004833 if (accessibilityScriptInjected()) {
Svetoslav Ganovb01c3d22011-01-11 14:19:17 -08004834 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
4835 // if an accessibility script is injected we delegate to it the key handling.
4836 // this script is a screen reader which is a fully fledged solution for blind
4837 // users to navigate in and interact with web pages.
4838 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
4839 return true;
4840 } else {
4841 // Clean up if accessibility was disabled after loading the current URL.
4842 mAccessibilityScriptInjected = false;
4843 }
4844 } else if (mAccessibilityInjector != null) {
4845 if (AccessibilityManager.getInstance(mContext).isEnabled()) {
4846 if (mAccessibilityInjector.onKeyEvent(event)) {
4847 // if an accessibility injector is present (no JavaScript enabled or the site
4848 // opts out injecting our JavaScript screen reader) we let it decide whether to
4849 // act on and consume the event.
4850 return true;
4851 }
4852 } else {
4853 // Clean up if accessibility was disabled after loading the current URL.
4854 mAccessibilityInjector = null;
4855 }
Svetoslav Ganov12bed782011-01-03 14:14:50 -08004856 }
4857
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004858 if (keyCode >= KeyEvent.KEYCODE_DPAD_UP
4859 && keyCode <= KeyEvent.KEYCODE_DPAD_RIGHT) {
Svetoslav Ganov12bed782011-01-03 14:14:50 -08004860 if (nativePageShouldHandleShiftAndArrows()) {
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07004861 letPageHandleNavKey(keyCode, event.getEventTime(), false, event.getMetaState());
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05004862 return true;
4863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004864 // always handle the navigation keys in the UI thread
4865 // Bubble up the key event as WebView doesn't handle it
4866 return false;
4867 }
4868
Cary Clarkaa86ac82010-12-28 11:30:18 -05004869 if (isEnterActionKey(keyCode)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004870 // remove the long press message first
Leon Scrogginse3225672009-06-03 15:53:13 -04004871 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
4872 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004873
Cary Clark924af702010-06-04 16:37:43 -04004874 if (mSelectingText) {
Cary Clarkc05af372009-10-16 10:52:27 -04004875 if (mExtendSelection) {
Cary Clark924af702010-06-04 16:37:43 -04004876 copySelection();
4877 selectionDone();
Cary Clarkc05af372009-10-16 10:52:27 -04004878 } else {
4879 mExtendSelection = true;
Cary Clark924af702010-06-04 16:37:43 -04004880 nativeSetExtendSelection();
Cary Clark09e383c2009-10-26 16:43:58 -04004881 invalidate(); // draw the i-beam instead of the arrow
Cary Clarkc05af372009-10-16 10:52:27 -04004882 }
4883 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004884 }
Grace Klobadd817492009-09-14 10:17:06 -07004885
4886 // perform the single click
4887 Rect visibleRect = sendOurVisibleRect();
4888 // Note that sendOurVisibleRect calls viewToContent, so the
4889 // coordinates should be in content coordinates.
4890 if (!nativeCursorIntersects(visibleRect)) {
4891 return false;
4892 }
Grace Klobadd817492009-09-14 10:17:06 -07004893 WebViewCore.CursorData data = cursorData();
4894 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE, data);
4895 playSoundEffect(SoundEffectConstants.CLICK);
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04004896 if (nativeCursorIsTextInput()) {
4897 rebuildWebTextView();
Leon Scroggins1d96ca02009-10-23 11:49:03 -04004898 centerKeyPressOnTextField();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04004899 if (inEditingMode()) {
4900 mWebTextView.setDefaultSelection();
Leon Scrogginsbb107bd2009-10-23 16:18:42 -04004901 }
Leon Scroggins1d60c5c2009-10-23 10:16:51 -04004902 return true;
4903 }
Leon Scroggins2aed7762010-08-13 17:11:42 -04004904 clearTextEntry();
Cary Clarke60cb7f2010-08-25 14:56:00 -04004905 nativeShowCursorTimed();
Cary Clarkddbda002011-01-06 13:40:15 -05004906 if (mCallbackProxy.uiOverrideUrlLoading(nativeCursorText())) {
4907 return true;
4908 }
4909 if (nativeCursorNodePointer() != 0 && !nativeCursorWantsKeyEvents()) {
Grace Klobadd817492009-09-14 10:17:06 -07004910 mWebViewCore.sendMessage(EventHub.CLICK, data.mFrame,
4911 nativeCursorNodePointer());
Cary Clarkddbda002011-01-06 13:40:15 -05004912 return true;
Grace Klobadd817492009-09-14 10:17:06 -07004913 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004914 }
4915
4916 // TODO: should we pass all the keys to DOM or check the meta tag
Cary Clark2f1d60c2009-06-03 08:05:53 -04004917 if (nativeCursorWantsKeyEvents() || true) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08004918 // pass the key to DOM
4919 mWebViewCore.sendMessage(EventHub.KEY_UP, event);
4920 // return true as DOM handles the key
4921 return true;
4922 }
4923
4924 // Bubble up the key event as WebView doesn't handle it
4925 return false;
4926 }
Cary Clarkd6982c92009-05-29 11:02:22 -04004927
Leon Scroggins8a4fd2f2010-12-16 18:17:23 -05004928 /*
Cary Clark03f00222011-02-10 19:37:15 -05004929 * Enter selecting text mode, and see if CAB should be shown.
4930 * Returns true if the WebView is now in
Leon Scroggins8a4fd2f2010-12-16 18:17:23 -05004931 * selecting text mode (including if it was already in that mode, and this
4932 * method did nothing).
4933 */
Cary Clark03f00222011-02-10 19:37:15 -05004934 private boolean setUpSelect(boolean selectWord, int x, int y) {
Leon Scroggins8a4fd2f2010-12-16 18:17:23 -05004935 if (0 == mNativeClass) return false; // client isn't initialized
4936 if (inFullScreenMode()) return false;
4937 if (mSelectingText) return true;
Cary Clark03f00222011-02-10 19:37:15 -05004938 nativeResetSelection();
4939 if (selectWord && !nativeWordSelection(x, y)) {
4940 selectionDone();
4941 return false;
4942 }
4943 mSelectCallback = new SelectActionModeCallback();
4944 mSelectCallback.setWebView(this);
4945 if (startActionMode(mSelectCallback) == null) {
4946 // There is no ActionMode, so do not allow the user to modify a
4947 // selection.
4948 selectionDone();
4949 return false;
4950 }
Cary Clark09e383c2009-10-26 16:43:58 -04004951 mExtendSelection = false;
Cary Clark924af702010-06-04 16:37:43 -04004952 mSelectingText = mDrawSelectionPointer = true;
4953 // don't let the picture change during text selection
4954 WebViewCore.pauseUpdatePicture(mWebViewCore);
Cary Clark09e383c2009-10-26 16:43:58 -04004955 if (nativeHasCursorNode()) {
4956 Rect rect = nativeCursorNodeBounds();
4957 mSelectX = contentToViewX(rect.left);
4958 mSelectY = contentToViewY(rect.top);
4959 } else if (mLastTouchY > getVisibleTitleHeight()) {
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08004960 mSelectX = mScrollX + mLastTouchX;
4961 mSelectY = mScrollY + mLastTouchY;
Cary Clark09e383c2009-10-26 16:43:58 -04004962 } else {
4963 mSelectX = mScrollX + getViewWidth() / 2;
4964 mSelectY = mScrollY + getViewHeightWithTitle() / 2;
4965 }
4966 nativeHideCursor();
Cary Clarkb9aaa772011-01-07 16:14:54 -05004967 mMinAutoScrollX = 0;
4968 mMaxAutoScrollX = getViewWidth();
4969 mMinAutoScrollY = 0;
4970 mMaxAutoScrollY = getViewHeightWithTitle();
4971 mScrollingLayer = nativeScrollableLayer(viewToContentX(mSelectX),
4972 viewToContentY(mSelectY), mScrollingLayerRect,
4973 mScrollingLayerBounds);
4974 if (mScrollingLayer != 0) {
4975 if (mScrollingLayerRect.left != mScrollingLayerRect.right) {
4976 mMinAutoScrollX = Math.max(mMinAutoScrollX,
4977 contentToViewX(mScrollingLayerBounds.left));
4978 mMaxAutoScrollX = Math.min(mMaxAutoScrollX,
4979 contentToViewX(mScrollingLayerBounds.right));
4980 }
4981 if (mScrollingLayerRect.top != mScrollingLayerRect.bottom) {
4982 mMinAutoScrollY = Math.max(mMinAutoScrollY,
4983 contentToViewY(mScrollingLayerBounds.top));
4984 mMaxAutoScrollY = Math.min(mMaxAutoScrollY,
4985 contentToViewY(mScrollingLayerBounds.bottom));
4986 }
4987 }
4988 mMinAutoScrollX += SELECT_SCROLL;
4989 mMaxAutoScrollX -= SELECT_SCROLL;
4990 mMinAutoScrollY += SELECT_SCROLL;
4991 mMaxAutoScrollY -= SELECT_SCROLL;
Leon Scroggins8a4fd2f2010-12-16 18:17:23 -05004992 return true;
Cary Clark09e383c2009-10-26 16:43:58 -04004993 }
4994
Cary Clark966641a2010-03-04 08:41:56 -05004995 /**
4996 * Use this method to put the WebView into text selection mode.
4997 * Do not rely on this functionality; it will be deprecated in the future.
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00004998 * @deprecated This method is now obsolete.
Cary Clark966641a2010-03-04 08:41:56 -05004999 */
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00005000 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005001 public void emulateShiftHeld() {
Cary Clark03f00222011-02-10 19:37:15 -05005002 setUpSelect(false, 0, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005003 }
5004
Cary Clark924af702010-06-04 16:37:43 -04005005 /**
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04005006 * Select all of the text in this WebView.
Cary Clark2cdee232010-12-29 14:59:24 -05005007 *
5008 * @hide pending API council approval.
Cary Clark924af702010-06-04 16:37:43 -04005009 */
Cary Clark2cdee232010-12-29 14:59:24 -05005010 public void selectAll() {
Cary Clark924af702010-06-04 16:37:43 -04005011 if (0 == mNativeClass) return; // client isn't initialized
5012 if (inFullScreenMode()) return;
Cary Clark2cdee232010-12-29 14:59:24 -05005013 if (!mSelectingText) {
5014 // retrieve a point somewhere within the text
5015 Point select = nativeSelectableText();
5016 if (!selectText(select.x, select.y)) return;
Leon Scroggins8a4fd2f2010-12-16 18:17:23 -05005017 }
Cary Clark924af702010-06-04 16:37:43 -04005018 nativeSelectAll();
5019 mDrawSelectionPointer = false;
5020 mExtendSelection = true;
5021 invalidate();
5022 }
5023
5024 /**
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04005025 * Called when the selection has been removed.
Cary Clark924af702010-06-04 16:37:43 -04005026 */
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04005027 void selectionDone() {
Cary Clark924af702010-06-04 16:37:43 -04005028 if (mSelectingText) {
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04005029 mSelectingText = false;
5030 // finish is idempotent, so this is fine even if selectionDone was
5031 // called by mSelectCallback.onDestroyActionMode
5032 mSelectCallback.finish();
5033 mSelectCallback = null;
Cary Clark0df02692010-11-24 11:01:37 -05005034 WebViewCore.resumePriority();
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04005035 WebViewCore.resumeUpdatePicture(mWebViewCore);
Cary Clark924af702010-06-04 16:37:43 -04005036 invalidate(); // redraw without selection
Cary Clark6f5dfc62010-11-11 13:09:20 -05005037 mAutoScrollX = 0;
5038 mAutoScrollY = 0;
5039 mSentAutoScrollMessage = false;
Cary Clark924af702010-06-04 16:37:43 -04005040 }
5041 }
5042
5043 /**
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04005044 * Copy the selection to the clipboard
Cary Clark2cdee232010-12-29 14:59:24 -05005045 *
5046 * @hide pending API council approval.
Cary Clark924af702010-06-04 16:37:43 -04005047 */
Cary Clark2cdee232010-12-29 14:59:24 -05005048 public boolean copySelection() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005049 boolean copiedSomething = false;
Cary Clark924af702010-06-04 16:37:43 -04005050 String selection = getSelection();
Cary Clark7170bb62011-01-12 10:12:15 -05005051 if (selection != null && selection != "") {
Cary Clark924af702010-06-04 16:37:43 -04005052 if (DebugFlags.WEB_VIEW) {
5053 Log.v(LOGTAG, "copySelection \"" + selection + "\"");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005054 }
Cary Clark924af702010-06-04 16:37:43 -04005055 Toast.makeText(mContext
5056 , com.android.internal.R.string.text_copied
5057 , Toast.LENGTH_SHORT).show();
5058 copiedSomething = true;
Dianne Hackborn90d5e8c2010-08-05 13:47:55 -07005059 ClipboardManager cm = (ClipboardManager)getContext()
5060 .getSystemService(Context.CLIPBOARD_SERVICE);
5061 cm.setText(selection);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005062 }
Cary Clark09e383c2009-10-26 16:43:58 -04005063 invalidate(); // remove selection region and pointer
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005064 return copiedSomething;
5065 }
5066
Cary Clark924af702010-06-04 16:37:43 -04005067 /**
Narayan Kamath9497c5f2011-02-22 12:05:34 +00005068 * @hide pending API Council approval.
5069 */
5070 public SearchBox getSearchBox() {
Michael Kolbb7481862011-03-03 17:09:19 -08005071 if ((mWebViewCore == null) || (mWebViewCore.getBrowserFrame() == null)) {
5072 return null;
5073 }
Narayan Kamath9497c5f2011-02-22 12:05:34 +00005074 return mWebViewCore.getBrowserFrame().getSearchBox();
5075 }
5076
5077 /**
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04005078 * Returns the currently highlighted text as a string.
Cary Clark924af702010-06-04 16:37:43 -04005079 */
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04005080 String getSelection() {
Cary Clark924af702010-06-04 16:37:43 -04005081 if (mNativeClass == 0) return "";
5082 return nativeGetSelection();
5083 }
5084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005085 @Override
5086 protected void onAttachedToWindow() {
5087 super.onAttachedToWindow();
Grace Kloba9b95ab12010-04-01 16:07:30 -07005088 if (hasWindowFocus()) setActive(true);
Teng-Hui Zhua7f76872010-11-29 11:15:32 -08005089 final ViewTreeObserver treeObserver = getViewTreeObserver();
Gilles Debunne0e7d652d2011-02-22 15:26:14 -08005090 if (mGlobalLayoutListener == null) {
5091 mGlobalLayoutListener = new InnerGlobalLayoutListener();
5092 treeObserver.addOnGlobalLayoutListener(mGlobalLayoutListener);
5093 }
5094 if (mScrollChangedListener == null) {
5095 mScrollChangedListener = new InnerScrollChangedListener();
5096 treeObserver.addOnScrollChangedListener(mScrollChangedListener);
Teng-Hui Zhua7f76872010-11-29 11:15:32 -08005097 }
Svetoslav Ganov4acac232011-02-02 15:22:24 -08005098
5099 addAccessibilityApisToJavaScript();
Adam Powell4fb35d42011-03-03 17:54:55 -08005100
5101 mTouchEventQueue.reset();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005102 }
5103
5104 @Override
5105 protected void onDetachedFromWindow() {
Leon Scroggins05919f22010-09-14 17:22:36 -04005106 clearHelpers();
Derek Sollenberger90b6e482010-05-10 12:38:54 -04005107 mZoomManager.dismissZoomPicker();
Grace Kloba9b95ab12010-04-01 16:07:30 -07005108 if (hasWindowFocus()) setActive(false);
Teng-Hui Zhua7f76872010-11-29 11:15:32 -08005109
5110 final ViewTreeObserver treeObserver = getViewTreeObserver();
Gilles Debunne0e7d652d2011-02-22 15:26:14 -08005111 if (mGlobalLayoutListener != null) {
5112 treeObserver.removeGlobalOnLayoutListener(mGlobalLayoutListener);
5113 mGlobalLayoutListener = null;
5114 }
5115 if (mScrollChangedListener != null) {
5116 treeObserver.removeOnScrollChangedListener(mScrollChangedListener);
5117 mScrollChangedListener = null;
Teng-Hui Zhua7f76872010-11-29 11:15:32 -08005118 }
5119
Svetoslav Ganov4acac232011-02-02 15:22:24 -08005120 removeAccessibilityApisFromJavaScript();
5121
Grace Kloba378f0282010-03-26 15:01:30 -07005122 super.onDetachedFromWindow();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005123 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005124
Steve Howard16bd9372010-04-12 14:46:09 -07005125 @Override
5126 protected void onVisibilityChanged(View changedView, int visibility) {
5127 super.onVisibilityChanged(changedView, visibility);
Derek Sollenbergerf39d2662010-06-24 16:41:04 -04005128 // The zoomManager may be null if the webview is created from XML that
5129 // specifies the view's visibility param as not visible (see http://b/2794841)
5130 if (visibility != View.VISIBLE && mZoomManager != null) {
Derek Sollenberger90b6e482010-05-10 12:38:54 -04005131 mZoomManager.dismissZoomPicker();
Steve Howard16bd9372010-04-12 14:46:09 -07005132 }
5133 }
5134
Leon Scrogginsa57632f2009-11-16 10:51:12 -05005135 /**
5136 * @deprecated WebView no longer needs to implement
5137 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
5138 */
5139 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005140 public void onChildViewAdded(View parent, View child) {}
Cary Clarkd6982c92009-05-29 11:02:22 -04005141
Leon Scrogginsa57632f2009-11-16 10:51:12 -05005142 /**
5143 * @deprecated WebView no longer needs to implement
5144 * ViewGroup.OnHierarchyChangeListener. This method does nothing now.
5145 */
5146 @Deprecated
5147 public void onChildViewRemoved(View p, View child) {}
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005148
5149 /**
5150 * @deprecated WebView should not have implemented
Gilles Debunne0e7d652d2011-02-22 15:26:14 -08005151 * ViewTreeObserver.OnGlobalFocusChangeListener. This method does nothing now.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005152 */
5153 @Deprecated
5154 public void onGlobalFocusChanged(View oldFocus, View newFocus) {
5155 }
5156
Leon Scroggins40777232011-01-18 16:49:20 -05005157 void setActive(boolean active) {
Grace Kloba9b95ab12010-04-01 16:07:30 -07005158 if (active) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005159 if (hasFocus()) {
5160 // If our window regained focus, and we have focus, then begin
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04005161 // drawing the cursor ring
Cary Clarkd6982c92009-05-29 11:02:22 -04005162 mDrawCursorRing = true;
Leon Scroggins2aed7762010-08-13 17:11:42 -04005163 setFocusControllerActive(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005164 if (mNativeClass != 0) {
5165 nativeRecordButtons(true, false, true);
5166 }
5167 } else {
Leon Scroggins2aed7762010-08-13 17:11:42 -04005168 if (!inEditingMode()) {
5169 // If our window gained focus, but we do not have it, do not
5170 // draw the cursor ring.
5171 mDrawCursorRing = false;
5172 setFocusControllerActive(false);
5173 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005174 // We do not call nativeRecordButtons here because we assume
5175 // that when we lost focus, or window focus, it got called with
5176 // false for the first parameter
5177 }
5178 } else {
Derek Sollenberger90b6e482010-05-10 12:38:54 -04005179 if (!mZoomManager.isZoomPickerVisible()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005180 /*
Derek Sollenberger90b6e482010-05-10 12:38:54 -04005181 * The external zoom controls come in their own window, so our
5182 * window loses focus. Our policy is to not draw the cursor ring
5183 * if our window is not focused, but this is an exception since
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005184 * the user can still navigate the web page with the zoom
5185 * controls showing.
5186 */
Cary Clarkd6982c92009-05-29 11:02:22 -04005187 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005188 }
Leon Scroggins115626a2011-02-17 12:00:48 -05005189 mKeysPressed.clear();
Grace Kloba378f0282010-03-26 15:01:30 -07005190 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
5191 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005192 if (mNativeClass != 0) {
5193 nativeRecordButtons(false, false, true);
5194 }
Leon Scroggins2aed7762010-08-13 17:11:42 -04005195 setFocusControllerActive(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005196 }
5197 invalidate();
Grace Kloba9b95ab12010-04-01 16:07:30 -07005198 }
5199
5200 // To avoid drawing the cursor ring, and remove the TextView when our window
5201 // loses focus.
5202 @Override
5203 public void onWindowFocusChanged(boolean hasWindowFocus) {
5204 setActive(hasWindowFocus);
5205 if (hasWindowFocus) {
Patrick Scott8a5d3352010-08-18 11:00:42 -04005206 JWebCoreJavaBridge.setActiveWebView(this);
Grace Kloba9b95ab12010-04-01 16:07:30 -07005207 } else {
Patrick Scott8a5d3352010-08-18 11:00:42 -04005208 JWebCoreJavaBridge.removeActiveWebView(this);
Grace Kloba9b95ab12010-04-01 16:07:30 -07005209 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005210 super.onWindowFocusChanged(hasWindowFocus);
5211 }
5212
Leon Scroggins63dda1c2009-04-15 13:25:00 -04005213 /*
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04005214 * Pass a message to WebCore Thread, telling the WebCore::Page's
5215 * FocusController to be "inactive" so that it will
5216 * not draw the blinking cursor. It gets set to "active" to draw the cursor
5217 * in WebViewCore.cpp, when the WebCore thread receives key events/clicks.
Leon Scroggins63dda1c2009-04-15 13:25:00 -04005218 */
Leon Scroggins2aed7762010-08-13 17:11:42 -04005219 /* package */ void setFocusControllerActive(boolean active) {
5220 if (mWebViewCore == null) return;
5221 mWebViewCore.sendMessage(EventHub.SET_ACTIVE, active ? 1 : 0, 0);
Leon Scroggins244d2d42010-12-06 16:29:38 -05005222 // Need to send this message after the document regains focus.
5223 if (active && mListBoxMessage != null) {
5224 mWebViewCore.sendMessage(mListBoxMessage);
5225 mListBoxMessage = null;
5226 }
Leon Scroggins63dda1c2009-04-15 13:25:00 -04005227 }
5228
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005229 @Override
5230 protected void onFocusChanged(boolean focused, int direction,
5231 Rect previouslyFocusedRect) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005232 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005233 Log.v(LOGTAG, "MT focusChanged " + focused + ", " + direction);
5234 }
5235 if (focused) {
5236 // When we regain focus, if we have window focus, resume drawing
Leon Scrogginsfa03cde2009-06-15 15:48:46 -04005237 // the cursor ring
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005238 if (hasWindowFocus()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005239 mDrawCursorRing = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005240 if (mNativeClass != 0) {
5241 nativeRecordButtons(true, false, true);
5242 }
Leon Scroggins2aed7762010-08-13 17:11:42 -04005243 setFocusControllerActive(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005244 //} else {
5245 // The WebView has gained focus while we do not have
5246 // windowfocus. When our window lost focus, we should have
5247 // called nativeRecordButtons(false...)
5248 }
5249 } else {
5250 // When we lost focus, unless focus went to the TextView (which is
Cary Clarkd6982c92009-05-29 11:02:22 -04005251 // true if we are in editing mode), stop drawing the cursor ring.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005252 if (!inEditingMode()) {
Cary Clarkd6982c92009-05-29 11:02:22 -04005253 mDrawCursorRing = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005254 if (mNativeClass != 0) {
5255 nativeRecordButtons(false, false, true);
5256 }
Leon Scrogginsb4ffd112011-01-24 17:43:47 -05005257 setFocusControllerActive(false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005258 }
Leon Scroggins115626a2011-02-17 12:00:48 -05005259 mKeysPressed.clear();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005260 }
5261
5262 super.onFocusChanged(focused, direction, previouslyFocusedRect);
5263 }
5264
Nicolas Roard12c18e62010-10-13 20:14:31 -07005265 void setGLRectViewport() {
Teng-Hui Zhue6fb0f12010-11-23 16:37:45 -08005266 // Use the getGlobalVisibleRect() to get the intersection among the parents
Chet Haase91fc3cf2011-01-28 00:20:04 -08005267 // visible == false means we're clipped - send a null rect down to indicate that
5268 // we should not draw
5269 boolean visible = getGlobalVisibleRect(mGLRectViewport);
5270 if (visible) {
5271 // Then need to invert the Y axis, just for GL
5272 View rootView = getRootView();
5273 int rootViewHeight = rootView.getHeight();
Romain Guycabfcc12011-03-07 18:06:46 -08005274 mViewRectViewport.set(mGLRectViewport);
Chet Haase91fc3cf2011-01-28 00:20:04 -08005275 int savedWebViewBottom = mGLRectViewport.bottom;
5276 mGLRectViewport.bottom = rootViewHeight - mGLRectViewport.top - getVisibleTitleHeight();
5277 mGLRectViewport.top = rootViewHeight - savedWebViewBottom;
5278 mGLViewportEmpty = false;
5279 } else {
5280 mGLViewportEmpty = true;
5281 }
Romain Guycabfcc12011-03-07 18:06:46 -08005282 nativeUpdateDrawGLFunction(mGLViewportEmpty ? null : mGLRectViewport,
5283 mGLViewportEmpty ? null : mViewRectViewport);
Nicolas Roard12c18e62010-10-13 20:14:31 -07005284 }
5285
Grace Kloba3f9faf42009-10-13 14:13:54 -07005286 /**
5287 * @hide
5288 */
5289 @Override
5290 protected boolean setFrame(int left, int top, int right, int bottom) {
5291 boolean changed = super.setFrame(left, top, right, bottom);
5292 if (!changed && mHeightCanMeasure) {
5293 // When mHeightCanMeasure is true, we will set mLastHeightSent to 0
5294 // in WebViewCore after we get the first layout. We do call
5295 // requestLayout() when we get contentSizeChanged(). But the View
5296 // system won't call onSizeChanged if the dimension is not changed.
5297 // In this case, we need to call sendViewSizeZoom() explicitly to
5298 // notify the WebKit about the new dimensions.
Derek Sollenberger03e48912010-05-18 17:03:42 -04005299 sendViewSizeZoom(false);
Grace Kloba3f9faf42009-10-13 14:13:54 -07005300 }
Nicolas Roard12c18e62010-10-13 20:14:31 -07005301 setGLRectViewport();
Grace Kloba3f9faf42009-10-13 14:13:54 -07005302 return changed;
5303 }
5304
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005305 @Override
5306 protected void onSizeChanged(int w, int h, int ow, int oh) {
5307 super.onSizeChanged(w, h, ow, oh);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005308
Grace Klobaa4fa1072009-11-09 12:01:50 -08005309 // adjust the max viewport width depending on the view dimensions. This
5310 // is to ensure the scaling is not going insane. So do not shrink it if
5311 // the view size is temporarily smaller, e.g. when soft keyboard is up.
Derek Sollenberger4aef6972010-06-24 15:03:43 -04005312 int newMaxViewportWidth = (int) (Math.max(w, h) / mZoomManager.getDefaultMinZoomScale());
Grace Klobaa4fa1072009-11-09 12:01:50 -08005313 if (newMaxViewportWidth > sMaxViewportWidth) {
5314 sMaxViewportWidth = newMaxViewportWidth;
5315 }
5316
Derek Sollenberger341e22f2010-06-02 12:34:34 -04005317 mZoomManager.onSizeChanged(w, h, ow, oh);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005318 }
5319
5320 @Override
5321 protected void onScrollChanged(int l, int t, int oldl, int oldt) {
5322 super.onScrollChanged(l, t, oldl, oldt);
Adam Powell637d3372010-08-25 14:37:03 -07005323 if (!mInOverScrollMode) {
5324 sendOurVisibleRect();
5325 // update WebKit if visible title bar height changed. The logic is same
5326 // as getVisibleTitleHeight.
5327 int titleHeight = getTitleHeight();
5328 if (Math.max(titleHeight - t, 0) != Math.max(titleHeight - oldt, 0)) {
5329 sendViewSizeZoom(false);
5330 }
Mike Reedb64f6f82010-03-23 10:29:37 -04005331 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005332 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005333
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005334 @Override
5335 public boolean dispatchKeyEvent(KeyEvent event) {
Leon Scroggins115626a2011-02-17 12:00:48 -05005336 switch (event.getAction()) {
5337 case KeyEvent.ACTION_DOWN:
5338 mKeysPressed.add(Integer.valueOf(event.getKeyCode()));
5339 break;
5340 case KeyEvent.ACTION_MULTIPLE:
5341 // Always accept the action.
5342 break;
5343 case KeyEvent.ACTION_UP:
5344 int location = mKeysPressed.indexOf(Integer.valueOf(event.getKeyCode()));
5345 if (location == -1) {
5346 // We did not receive the key down for this key, so do not
5347 // handle the key up.
Leon Scrogginsdefba0c2011-02-17 13:51:50 -05005348 return false;
Leon Scroggins115626a2011-02-17 12:00:48 -05005349 } else {
5350 // We did receive the key down. Handle the key up, and
5351 // remove it from our pressed keys.
5352 mKeysPressed.remove(location);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005353 }
Leon Scroggins115626a2011-02-17 12:00:48 -05005354 break;
5355 default:
5356 // Accept the action. This should not happen, unless a new
5357 // action is added to KeyEvent.
5358 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005359 }
Leon Scrogginsdefba0c2011-02-17 13:51:50 -05005360 if (inEditingMode() && mWebTextView.isFocused()) {
5361 // Ensure that the WebTextView gets the event, even if it does
5362 // not currently have a bounds.
5363 return mWebTextView.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005364 } else {
Leon Scrogginsdefba0c2011-02-17 13:51:50 -05005365 return super.dispatchKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005366 }
5367 }
5368
5369 // Here are the snap align logic:
5370 // 1. If it starts nearly horizontally or vertically, snap align;
5371 // 2. If there is a dramitic direction change, let it go;
5372 // 3. If there is a same direction back and forth, lock it.
5373
5374 // adjustable parameters
5375 private int mMinLockSnapReverseDistance;
5376 private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
5377 private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
5378
Grace Klobac2242f22010-03-05 14:00:26 -08005379 private boolean hitFocusedPlugin(int contentX, int contentY) {
Cary Clarke84a0db2010-03-17 15:58:20 -04005380 if (DebugFlags.WEB_VIEW) {
5381 Log.v(LOGTAG, "nativeFocusIsPlugin()=" + nativeFocusIsPlugin());
5382 Rect r = nativeFocusNodeBounds();
5383 Log.v(LOGTAG, "nativeFocusNodeBounds()=(" + r.left + ", " + r.top
5384 + ", " + r.right + ", " + r.bottom + ")");
5385 }
Grace Klobac2242f22010-03-05 14:00:26 -08005386 return nativeFocusIsPlugin()
Cary Clarke84a0db2010-03-17 15:58:20 -04005387 && nativeFocusNodeBounds().contains(contentX, contentY);
Grace Klobac2242f22010-03-05 14:00:26 -08005388 }
5389
5390 private boolean shouldForwardTouchEvent() {
5391 return mFullScreenHolder != null || (mForwardTouchEvents
Cary Clark924af702010-06-04 16:37:43 -04005392 && !mSelectingText
Adam Powella0def722011-03-14 17:54:10 -07005393 && mPreventDefault != PREVENT_DEFAULT_IGNORE
5394 && mPreventDefault != PREVENT_DEFAULT_NO);
Grace Klobac2242f22010-03-05 14:00:26 -08005395 }
5396
5397 private boolean inFullScreenMode() {
5398 return mFullScreenHolder != null;
5399 }
5400
Derek Sollenbergerfa89c502010-10-01 11:19:36 -04005401 private void dismissFullScreenMode() {
5402 if (inFullScreenMode()) {
5403 mFullScreenHolder.dismiss();
5404 mFullScreenHolder = null;
5405 }
5406 }
5407
Derek Sollenberger293c3602010-06-04 10:44:48 -04005408 void onPinchToZoomAnimationStart() {
5409 // cancel the single touch handling
5410 cancelTouch();
5411 onZoomAnimationStart();
5412 }
5413
5414 void onPinchToZoomAnimationEnd(ScaleGestureDetector detector) {
5415 onZoomAnimationEnd();
5416 // start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as
5417 // it may trigger the unwanted click, can't use TOUCH_DRAG_MODE
5418 // as it may trigger the unwanted fling.
5419 mTouchMode = TOUCH_PINCH_DRAG;
5420 mConfirmMove = true;
5421 startTouch(detector.getFocusX(), detector.getFocusY(), mLastTouchTime);
5422 }
5423
Patrick Scott62310912010-12-06 17:44:50 -05005424 // See if there is a layer at x, y and switch to TOUCH_DRAG_LAYER_MODE if a
5425 // layer is found.
Patrick Scott4b903782010-11-30 08:14:05 -05005426 private void startScrollingLayer(float x, float y) {
Patrick Scott62310912010-12-06 17:44:50 -05005427 int contentX = viewToContentX((int) x + mScrollX);
5428 int contentY = viewToContentY((int) y + mScrollY);
5429 mScrollingLayer = nativeScrollableLayer(contentX, contentY,
Cary Clarkb9aaa772011-01-07 16:14:54 -05005430 mScrollingLayerRect, mScrollingLayerBounds);
Patrick Scott62310912010-12-06 17:44:50 -05005431 if (mScrollingLayer != 0) {
5432 mTouchMode = TOUCH_DRAG_LAYER_MODE;
Patrick Scotta3ebcc92010-07-16 11:52:22 -04005433 }
5434 }
5435
5436 // 1/(density * density) used to compute the distance between points.
5437 // Computed in init().
5438 private float DRAG_LAYER_INVERSE_DENSITY_SQUARED;
5439
5440 // The distance between two points reported in onTouchEvent scaled by the
5441 // density of the screen.
5442 private static final int DRAG_LAYER_FINGER_DISTANCE = 20000;
5443
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005444 @Override
5445 public boolean onTouchEvent(MotionEvent ev) {
Andy Stadlerb34fd1ff2010-09-27 15:51:09 -07005446 if (mNativeClass == 0 || (!isClickable() && !isLongClickable())) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005447 return false;
5448 }
5449
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04005450 if (DebugFlags.WEB_VIEW) {
Huahui Wu41865f42010-09-02 13:41:41 -07005451 Log.v(LOGTAG, ev + " at " + ev.getEventTime()
5452 + " mTouchMode=" + mTouchMode
5453 + " numPointers=" + ev.getPointerCount());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005454 }
5455
Adam Powell4fb35d42011-03-03 17:54:55 -08005456 // If WebKit wasn't interested in this multitouch gesture, enqueue
5457 // the event for handling directly rather than making the round trip
5458 // to WebKit and back.
5459 if (ev.getPointerCount() > 1 && mPreventDefault != PREVENT_DEFAULT_NO) {
5460 passMultiTouchToWebKit(ev, mTouchEventQueue.nextTouchSequence());
Adam Powelle33cef82011-02-23 16:51:20 -08005461 } else {
Adam Powell4fb35d42011-03-03 17:54:55 -08005462 mTouchEventQueue.enqueueTouchEvent(ev);
Huahui Wu41865f42010-09-02 13:41:41 -07005463 }
5464
Adam Powell4fb35d42011-03-03 17:54:55 -08005465 // Since all events are handled asynchronously, we always want the gesture stream.
5466 return true;
Huahui Wu0904c0d2011-01-07 17:30:53 -08005467 }
Patrick Scott4b903782010-11-30 08:14:05 -05005468
Huahui Wuf147d452011-01-12 14:02:36 -08005469 /*
5470 * Common code for single touch and multi-touch.
5471 * (x, y) denotes current focus point, which is the touch point for single touch
5472 * and the middle point for multi-touch.
5473 */
Huahui Wu1f301252011-01-19 17:32:32 -08005474 private boolean handleTouchEventCommon(MotionEvent ev, int action, int x, int y) {
Huahui Wu0904c0d2011-01-07 17:30:53 -08005475 long eventTime = ev.getEventTime();
Patrick Scotta3ebcc92010-07-16 11:52:22 -04005476
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005477 // Due to the touch screen edge effect, a touch closer to the edge
5478 // always snapped to the edge. As getViewWidth() can be different from
5479 // getWidth() due to the scrollbar, adjusting the point to match
5480 // getViewWidth(). Same applied to the height.
Patrick Scotta3ebcc92010-07-16 11:52:22 -04005481 x = Math.min(x, getViewWidth() - 1);
5482 y = Math.min(y, getViewHeightWithTitle() - 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005483
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08005484 int deltaX = mLastTouchX - x;
5485 int deltaY = mLastTouchY - y;
5486 int contentX = viewToContentX(x + mScrollX);
5487 int contentY = viewToContentY(y + mScrollY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005488
5489 switch (action) {
5490 case MotionEvent.ACTION_DOWN: {
Grace Klobac2242f22010-03-05 14:00:26 -08005491 mPreventDefault = PREVENT_DEFAULT_NO;
5492 mConfirmMove = false;
Cary Clarkb8491342010-11-29 16:23:19 -05005493 mInitialHitTestResult = null;
Grace Kloba04b28682009-09-14 14:38:37 -07005494 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04005495 // stop the current scroll animation, but if this is
5496 // the start of a fling, allow it to add to the current
5497 // fling's velocity
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005498 mScroller.abortAnimation();
5499 mTouchMode = TOUCH_DRAG_START_MODE;
Grace Klobac2242f22010-03-05 14:00:26 -08005500 mConfirmMove = true;
Grace Kloba96949ef2010-01-25 09:53:01 -08005501 mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005502 } else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
5503 mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
Grace Kloba178db412010-05-18 22:22:23 -07005504 if (getSettings().supportTouchOnly()) {
5505 removeTouchHighlight(true);
5506 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005507 if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
5508 mTouchMode = TOUCH_DOUBLE_TAP_MODE;
5509 } else {
5510 // commit the short press action for the previous tap
5511 doShortPress();
Grace Klobac2242f22010-03-05 14:00:26 -08005512 mTouchMode = TOUCH_INIT_MODE;
5513 mDeferTouchProcess = (!inFullScreenMode()
5514 && mForwardTouchEvents) ? hitFocusedPlugin(
5515 contentX, contentY) : false;
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005516 }
Grace Klobac2242f22010-03-05 14:00:26 -08005517 } else { // the normal case
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005518 mTouchMode = TOUCH_INIT_MODE;
Grace Klobac2242f22010-03-05 14:00:26 -08005519 mDeferTouchProcess = (!inFullScreenMode()
5520 && mForwardTouchEvents) ? hitFocusedPlugin(
5521 contentX, contentY) : false;
Cary Clark77d98f42009-07-31 09:40:38 -04005522 mWebViewCore.sendMessage(
5523 EventHub.UPDATE_FRAME_CACHE_IF_LOADING);
Grace Kloba178db412010-05-18 22:22:23 -07005524 if (getSettings().supportTouchOnly()) {
5525 TouchHighlightData data = new TouchHighlightData();
5526 data.mX = contentX;
5527 data.mY = contentY;
5528 data.mSlop = viewToContentDimension(mNavSlop);
5529 mWebViewCore.sendMessageDelayed(
5530 EventHub.GET_TOUCH_HIGHLIGHT_RECTS, data,
5531 ViewConfiguration.getTapTimeout());
5532 if (DEBUG_TOUCH_HIGHLIGHT) {
5533 if (getSettings().getNavDump()) {
5534 mTouchHighlightX = (int) x + mScrollX;
5535 mTouchHighlightY = (int) y + mScrollY;
5536 mPrivateHandler.postDelayed(new Runnable() {
5537 public void run() {
5538 mTouchHighlightX = mTouchHighlightY = 0;
5539 invalidate();
5540 }
5541 }, TOUCH_HIGHLIGHT_ELAPSE_TIME);
5542 }
5543 }
5544 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005545 if (mLogEvent && eventTime - mLastTouchUpTime < 1000) {
Dan Egnor18e93962010-02-10 19:27:58 -08005546 EventLog.writeEvent(EventLogTags.BROWSER_DOUBLE_TAP_DURATION,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005547 (eventTime - mLastTouchUpTime), eventTime);
5548 }
Cary Clark924af702010-06-04 16:37:43 -04005549 if (mSelectingText) {
5550 mDrawSelectionPointer = false;
5551 mSelectionStarted = nativeStartSelection(contentX, contentY);
5552 if (DebugFlags.WEB_VIEW) {
5553 Log.v(LOGTAG, "select=" + contentX + "," + contentY);
5554 }
5555 invalidate();
5556 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005557 }
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07005558 // Trigger the link
Cary Clark43f608f2011-02-18 11:30:12 -05005559 if (!mSelectingText && (mTouchMode == TOUCH_INIT_MODE
5560 || mTouchMode == TOUCH_DOUBLE_TAP_MODE)) {
Grace Klobac2242f22010-03-05 14:00:26 -08005561 mPrivateHandler.sendEmptyMessageDelayed(
5562 SWITCH_TO_SHORTPRESS, TAP_TIMEOUT);
5563 mPrivateHandler.sendEmptyMessageDelayed(
5564 SWITCH_TO_LONGPRESS, LONG_PRESS_TIMEOUT);
5565 if (inFullScreenMode() || mDeferTouchProcess) {
5566 mPreventDefault = PREVENT_DEFAULT_YES;
5567 } else if (mForwardTouchEvents) {
5568 mPreventDefault = PREVENT_DEFAULT_MAYBE_YES;
5569 } else {
5570 mPreventDefault = PREVENT_DEFAULT_NO;
5571 }
5572 // pass the touch events from UI thread to WebCore thread
5573 if (shouldForwardTouchEvent()) {
5574 TouchEventData ted = new TouchEventData();
5575 ted.mAction = action;
Huahui Wue838a422011-01-13 16:03:43 -08005576 ted.mIds = new int[1];
5577 ted.mIds[0] = ev.getPointerId(0);
Huahui Wu41865f42010-09-02 13:41:41 -07005578 ted.mPoints = new Point[1];
5579 ted.mPoints[0] = new Point(contentX, contentY);
Huahui Wu88b869a2011-03-17 17:42:12 -07005580 ted.mPointsInView = new Point[1];
5581 ted.mPointsInView[0] = new Point(x, y);
Grace Klobac2242f22010-03-05 14:00:26 -08005582 ted.mMetaState = ev.getMetaState();
5583 ted.mReprocess = mDeferTouchProcess;
Patrick Scottcfa734a2011-02-22 11:19:02 -05005584 ted.mNativeLayer = nativeScrollableLayer(
5585 contentX, contentY, ted.mNativeLayerRect, null);
Adam Powellae9d2642011-03-08 16:00:30 -08005586 ted.mSequence = mTouchEventQueue.nextTouchSequence();
Adam Powell3c534772011-04-04 14:27:12 -07005587 mTouchEventQueue.preQueueTouchEventData(ted);
Grace Klobac2242f22010-03-05 14:00:26 -08005588 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
5589 if (mDeferTouchProcess) {
5590 // still needs to set them for compute deltaX/Y
5591 mLastTouchX = x;
5592 mLastTouchY = y;
5593 break;
5594 }
5595 if (!inFullScreenMode()) {
Ben Murdocha3f055e2010-05-26 18:46:56 +01005596 mPrivateHandler.removeMessages(PREVENT_DEFAULT_TIMEOUT);
Grace Klobac2242f22010-03-05 14:00:26 -08005597 mPrivateHandler.sendMessageDelayed(mPrivateHandler
5598 .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
5599 action, 0), TAP_TIMEOUT);
5600 }
5601 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005602 }
Grace Kloba3a0def22010-01-23 21:11:54 -08005603 startTouch(x, y, eventTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005604 break;
5605 }
5606 case MotionEvent.ACTION_MOVE: {
Grace Klobac2242f22010-03-05 14:00:26 -08005607 boolean firstMove = false;
5608 if (!mConfirmMove && (deltaX * deltaX + deltaY * deltaY)
5609 >= mTouchSlopSquare) {
5610 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
5611 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
5612 mConfirmMove = true;
5613 firstMove = true;
5614 if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
5615 mTouchMode = TOUCH_INIT_MODE;
5616 }
Grace Kloba178db412010-05-18 22:22:23 -07005617 if (getSettings().supportTouchOnly()) {
5618 removeTouchHighlight(true);
5619 }
Grace Klobac2242f22010-03-05 14:00:26 -08005620 }
5621 // pass the touch events from UI thread to WebCore thread
5622 if (shouldForwardTouchEvent() && mConfirmMove && (firstMove
5623 || eventTime - mLastSentTouchTime > mCurrentTouchInterval)) {
5624 TouchEventData ted = new TouchEventData();
5625 ted.mAction = action;
Huahui Wue838a422011-01-13 16:03:43 -08005626 ted.mIds = new int[1];
5627 ted.mIds[0] = ev.getPointerId(0);
Huahui Wu41865f42010-09-02 13:41:41 -07005628 ted.mPoints = new Point[1];
5629 ted.mPoints[0] = new Point(contentX, contentY);
Huahui Wu88b869a2011-03-17 17:42:12 -07005630 ted.mPointsInView = new Point[1];
5631 ted.mPointsInView[0] = new Point(x, y);
Grace Klobac2242f22010-03-05 14:00:26 -08005632 ted.mMetaState = ev.getMetaState();
5633 ted.mReprocess = mDeferTouchProcess;
Patrick Scottcfa734a2011-02-22 11:19:02 -05005634 ted.mNativeLayer = mScrollingLayer;
5635 ted.mNativeLayerRect.set(mScrollingLayerRect);
Adam Powellae9d2642011-03-08 16:00:30 -08005636 ted.mSequence = mTouchEventQueue.nextTouchSequence();
Adam Powell3c534772011-04-04 14:27:12 -07005637 mTouchEventQueue.preQueueTouchEventData(ted);
Grace Klobac2242f22010-03-05 14:00:26 -08005638 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
5639 mLastSentTouchTime = eventTime;
5640 if (mDeferTouchProcess) {
5641 break;
5642 }
5643 if (firstMove && !inFullScreenMode()) {
5644 mPrivateHandler.sendMessageDelayed(mPrivateHandler
5645 .obtainMessage(PREVENT_DEFAULT_TIMEOUT,
5646 action, 0), TAP_TIMEOUT);
5647 }
5648 }
5649 if (mTouchMode == TOUCH_DONE_MODE
5650 || mPreventDefault == PREVENT_DEFAULT_YES) {
5651 // no dragging during scroll zoom animation, or when prevent
5652 // default is yes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005653 break;
5654 }
Grace Klobac2242f22010-03-05 14:00:26 -08005655 if (mVelocityTracker == null) {
5656 Log.e(LOGTAG, "Got null mVelocityTracker when "
5657 + "mPreventDefault = " + mPreventDefault
5658 + " mDeferTouchProcess = " + mDeferTouchProcess
5659 + " mTouchMode = " + mTouchMode);
Huahui Wu8465cc92011-01-12 10:17:19 -08005660 } else {
5661 mVelocityTracker.addMovement(ev);
Grace Klobac2242f22010-03-05 14:00:26 -08005662 }
Cary Clark924af702010-06-04 16:37:43 -04005663 if (mSelectingText && mSelectionStarted) {
5664 if (DebugFlags.WEB_VIEW) {
5665 Log.v(LOGTAG, "extend=" + contentX + "," + contentY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005666 }
Leon Scroggins7f7bd522010-11-05 11:23:31 -04005667 ViewParent parent = getParent();
5668 if (parent != null) {
5669 parent.requestDisallowInterceptTouchEvent(true);
5670 }
Cary Clarkb9aaa772011-01-07 16:14:54 -05005671 mAutoScrollX = x <= mMinAutoScrollX ? -SELECT_SCROLL
5672 : x >= mMaxAutoScrollX ? SELECT_SCROLL : 0;
5673 mAutoScrollY = y <= mMinAutoScrollY ? -SELECT_SCROLL
5674 : y >= mMaxAutoScrollY ? SELECT_SCROLL : 0;
5675 if ((mAutoScrollX != 0 || mAutoScrollY != 0)
5676 && !mSentAutoScrollMessage) {
5677 mSentAutoScrollMessage = true;
5678 mPrivateHandler.sendEmptyMessageDelayed(
5679 SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
Cary Clark6f5dfc62010-11-11 13:09:20 -05005680 }
Cary Clarkaa86ac82010-12-28 11:30:18 -05005681 if (deltaX != 0 || deltaY != 0) {
5682 nativeExtendSelection(contentX, contentY);
5683 invalidate();
5684 }
Cary Clark924af702010-06-04 16:37:43 -04005685 break;
5686 }
Patrick Scotta3ebcc92010-07-16 11:52:22 -04005687
5688 if (mTouchMode != TOUCH_DRAG_MODE &&
5689 mTouchMode != TOUCH_DRAG_LAYER_MODE) {
Ben Murdochfc0bcdd2010-04-23 10:55:29 -07005690
Grace Klobac2242f22010-03-05 14:00:26 -08005691 if (!mConfirmMove) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005692 break;
5693 }
Ben Murdochfc0bcdd2010-04-23 10:55:29 -07005694
Grace Klobac2242f22010-03-05 14:00:26 -08005695 if (mPreventDefault == PREVENT_DEFAULT_MAYBE_YES
5696 || mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
Grace Klobaf58af622009-09-24 17:41:23 -07005697 // track mLastTouchTime as we may need to do fling at
5698 // ACTION_UP
5699 mLastTouchTime = eventTime;
5700 break;
5701 }
Adam Powell048a3a52010-09-08 23:59:37 -07005702
Huahui Wuc0109242011-01-14 12:51:12 -08005703 // Only lock dragging to one axis if we don't have a scale in progress.
5704 // Scaling implies free-roaming movement. Note this is only ever a question
5705 // if mZoomManager.supportsPanDuringZoom() is true.
5706 final ScaleGestureDetector detector =
5707 mZoomManager.getMultiTouchGestureDetector();
5708 if (detector == null || !detector.isInProgress()) {
5709 // if it starts nearly horizontal or vertical, enforce it
5710 int ax = Math.abs(deltaX);
5711 int ay = Math.abs(deltaY);
5712 if (ax > MAX_SLOPE_FOR_DIAG * ay) {
5713 mSnapScrollMode = SNAP_X;
5714 mSnapPositive = deltaX > 0;
5715 } else if (ay > MAX_SLOPE_FOR_DIAG * ax) {
5716 mSnapScrollMode = SNAP_Y;
5717 mSnapPositive = deltaY > 0;
5718 }
5719 }
5720
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005721 mTouchMode = TOUCH_DRAG_MODE;
Romain Guyf7b4acc2009-12-01 16:24:45 -08005722 mLastTouchX = x;
5723 mLastTouchY = y;
Romain Guyf7b4acc2009-12-01 16:24:45 -08005724 deltaX = 0;
5725 deltaY = 0;
5726
Patrick Scott62310912010-12-06 17:44:50 -05005727 startScrollingLayer(x, y);
Grace Klobac2242f22010-03-05 14:00:26 -08005728 startDrag();
5729 }
5730
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005731 // do pan
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005732 boolean done = false;
Cary Clark25415e22009-10-12 13:41:28 -04005733 boolean keepScrollBarsVisible = false;
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08005734 if (deltaX == 0 && deltaY == 0) {
Cary Clark25415e22009-10-12 13:41:28 -04005735 keepScrollBarsVisible = done = true;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005736 } else {
5737 if (mSnapScrollMode == SNAP_X || mSnapScrollMode == SNAP_Y) {
5738 int ax = Math.abs(deltaX);
5739 int ay = Math.abs(deltaY);
5740 if (mSnapScrollMode == SNAP_X) {
5741 // radical change means getting out of snap mode
5742 if (ay > MAX_SLOPE_FOR_DIAG * ax
5743 && ay > MIN_BREAK_SNAP_CROSS_DISTANCE) {
5744 mSnapScrollMode = SNAP_NONE;
5745 }
5746 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04005747 if (ax > MAX_SLOPE_FOR_DIAG * ay &&
5748 (mSnapPositive
5749 ? deltaX < -mMinLockSnapReverseDistance
5750 : deltaX > mMinLockSnapReverseDistance)) {
5751 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005752 }
5753 } else {
5754 // radical change means getting out of snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04005755 if (ax > MAX_SLOPE_FOR_DIAG * ay
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005756 && ax > MIN_BREAK_SNAP_CROSS_DISTANCE) {
5757 mSnapScrollMode = SNAP_NONE;
5758 }
5759 // reverse direction means lock in the snap mode
Cary Clarkac492e12009-10-14 14:53:37 -04005760 if (ay > MAX_SLOPE_FOR_DIAG * ax &&
5761 (mSnapPositive
5762 ? deltaY < -mMinLockSnapReverseDistance
5763 : deltaY > mMinLockSnapReverseDistance)) {
5764 mSnapScrollMode |= SNAP_LOCK;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005765 }
5766 }
5767 }
Cary Clarkac492e12009-10-14 14:53:37 -04005768 if (mSnapScrollMode != SNAP_NONE) {
5769 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
5770 deltaY = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07005771 } else {
Cary Clarkac492e12009-10-14 14:53:37 -04005772 deltaX = 0;
Grace Kloba5b2c0562009-09-30 10:17:13 -07005773 }
Cary Clarkac492e12009-10-14 14:53:37 -04005774 }
5775 if ((deltaX | deltaY) != 0) {
Cary Clarkac492e12009-10-14 14:53:37 -04005776 if (deltaX != 0) {
5777 mLastTouchX = x;
5778 }
5779 if (deltaY != 0) {
5780 mLastTouchY = y;
5781 }
5782 mHeldMotionless = MOTIONLESS_FALSE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005783 }
5784 mLastTouchTime = eventTime;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005785 }
The Android Open Source Project10592532009-03-18 17:39:46 -07005786
Grace Klobac2242f22010-03-05 14:00:26 -08005787 doDrag(deltaX, deltaY);
Mike Reed19f3f0e2009-11-12 12:50:20 -05005788
Patrick Scotta3ebcc92010-07-16 11:52:22 -04005789 // Turn off scrollbars when dragging a layer.
5790 if (keepScrollBarsVisible &&
5791 mTouchMode != TOUCH_DRAG_LAYER_MODE) {
Cary Clark25415e22009-10-12 13:41:28 -04005792 if (mHeldMotionless != MOTIONLESS_TRUE) {
5793 mHeldMotionless = MOTIONLESS_TRUE;
5794 invalidate();
5795 }
Grace Kloba5b2c0562009-09-30 10:17:13 -07005796 // keep the scrollbar on the screen even there is no scroll
5797 awakenScrollBars(ViewConfiguration.getScrollDefaultDelay(),
5798 false);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005799 // return false to indicate that we can't pan out of the
5800 // view space
Cary Clark25415e22009-10-12 13:41:28 -04005801 return !done;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005802 }
5803 break;
5804 }
5805 case MotionEvent.ACTION_UP: {
Leon Scroggins IIIae9f8e42010-04-14 16:06:06 -04005806 if (!isFocused()) requestFocus();
Grace Klobac2242f22010-03-05 14:00:26 -08005807 // pass the touch events from UI thread to WebCore thread
5808 if (shouldForwardTouchEvent()) {
5809 TouchEventData ted = new TouchEventData();
Huahui Wue838a422011-01-13 16:03:43 -08005810 ted.mIds = new int[1];
5811 ted.mIds[0] = ev.getPointerId(0);
Grace Klobac2242f22010-03-05 14:00:26 -08005812 ted.mAction = action;
Huahui Wu41865f42010-09-02 13:41:41 -07005813 ted.mPoints = new Point[1];
5814 ted.mPoints[0] = new Point(contentX, contentY);
Huahui Wu88b869a2011-03-17 17:42:12 -07005815 ted.mPointsInView = new Point[1];
5816 ted.mPointsInView[0] = new Point(x, y);
Grace Klobac2242f22010-03-05 14:00:26 -08005817 ted.mMetaState = ev.getMetaState();
5818 ted.mReprocess = mDeferTouchProcess;
Patrick Scottcfa734a2011-02-22 11:19:02 -05005819 ted.mNativeLayer = mScrollingLayer;
5820 ted.mNativeLayerRect.set(mScrollingLayerRect);
Adam Powellae9d2642011-03-08 16:00:30 -08005821 ted.mSequence = mTouchEventQueue.nextTouchSequence();
Adam Powell3c534772011-04-04 14:27:12 -07005822 mTouchEventQueue.preQueueTouchEventData(ted);
Grace Klobac2242f22010-03-05 14:00:26 -08005823 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Mike Reed19f3f0e2009-11-12 12:50:20 -05005824 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005825 mLastTouchUpTime = eventTime;
Cary Clarkb9aaa772011-01-07 16:14:54 -05005826 if (mSentAutoScrollMessage) {
5827 mAutoScrollX = mAutoScrollY = 0;
5828 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005829 switch (mTouchMode) {
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005830 case TOUCH_DOUBLE_TAP_MODE: // double tap
5831 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobac2242f22010-03-05 14:00:26 -08005832 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
5833 if (inFullScreenMode() || mDeferTouchProcess) {
5834 TouchEventData ted = new TouchEventData();
Huahui Wue838a422011-01-13 16:03:43 -08005835 ted.mIds = new int[1];
5836 ted.mIds[0] = ev.getPointerId(0);
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005837 ted.mAction = WebViewCore.ACTION_DOUBLETAP;
Huahui Wu41865f42010-09-02 13:41:41 -07005838 ted.mPoints = new Point[1];
5839 ted.mPoints[0] = new Point(contentX, contentY);
Huahui Wu88b869a2011-03-17 17:42:12 -07005840 ted.mPointsInView = new Point[1];
5841 ted.mPointsInView[0] = new Point(x, y);
Ben Murdoch8a032a32010-02-02 18:20:11 +00005842 ted.mMetaState = ev.getMetaState();
Grace Klobac2242f22010-03-05 14:00:26 -08005843 ted.mReprocess = mDeferTouchProcess;
Patrick Scottcfa734a2011-02-22 11:19:02 -05005844 ted.mNativeLayer = nativeScrollableLayer(
5845 contentX, contentY,
5846 ted.mNativeLayerRect, null);
Adam Powellae9d2642011-03-08 16:00:30 -08005847 ted.mSequence = mTouchEventQueue.nextTouchSequence();
Adam Powell3c534772011-04-04 14:27:12 -07005848 mTouchEventQueue.preQueueTouchEventData(ted);
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005849 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Grace Klobac2242f22010-03-05 14:00:26 -08005850 } else if (mPreventDefault != PREVENT_DEFAULT_YES){
Derek Sollenberger15c5ddb2010-06-10 12:31:29 -04005851 mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
Grace Klobac2242f22010-03-05 14:00:26 -08005852 mTouchMode = TOUCH_DONE_MODE;
Grace Kloba5f68d6f2009-12-08 18:42:54 -08005853 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07005854 break;
Grace Klobaf58af622009-09-24 17:41:23 -07005855 case TOUCH_INIT_MODE: // tap
Grace Klobad66d84f2009-09-27 14:48:07 -07005856 case TOUCH_SHORTPRESS_START_MODE:
5857 case TOUCH_SHORTPRESS_MODE:
Grace Klobaf58af622009-09-24 17:41:23 -07005858 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
Grace Klobad66d84f2009-09-27 14:48:07 -07005859 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Grace Klobac2242f22010-03-05 14:00:26 -08005860 if (mConfirmMove) {
Grace Klobaf58af622009-09-24 17:41:23 -07005861 Log.w(LOGTAG, "Miss a drag as we are waiting for" +
5862 " WebCore's response for touch down.");
Grace Klobac2242f22010-03-05 14:00:26 -08005863 if (mPreventDefault != PREVENT_DEFAULT_YES
Grace Klobad7625dd2010-03-04 11:46:12 -08005864 && (computeMaxScrollX() > 0
5865 || computeMaxScrollY() > 0)) {
Ben Murdochfc0bcdd2010-04-23 10:55:29 -07005866 // If the user has performed a very quick touch
5867 // sequence it is possible that we may get here
5868 // before WebCore has had a chance to process the events.
5869 // In this case, any call to preventDefault in the
5870 // JS touch handler will not have been executed yet.
5871 // Hence we will see both the UI (now) and WebCore
5872 // (when context switches) handling the event,
5873 // regardless of whether the web developer actually
5874 // doeses preventDefault in their touch handler. This
5875 // is the nature of our asynchronous touch model.
5876
Grace Klobaf58af622009-09-24 17:41:23 -07005877 // we will not rewrite drag code here, but we
5878 // will try fling if it applies.
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005879 WebViewCore.reducePriority();
Grace Kloba524aab572010-04-07 11:12:52 -07005880 // to get better performance, pause updating the
5881 // picture
5882 WebViewCore.pauseUpdatePicture(mWebViewCore);
Grace Klobaf58af622009-09-24 17:41:23 -07005883 // fall through to TOUCH_DRAG_MODE
5884 } else {
Grace Klobac36862d52010-04-19 10:16:42 -07005885 // WebKit may consume the touch event and modify
5886 // DOM. drawContentPicture() will be called with
5887 // animateSroll as true for better performance.
5888 // Force redraw in high-quality.
5889 invalidate();
Grace Klobaf58af622009-09-24 17:41:23 -07005890 break;
5891 }
5892 } else {
Cary Clark924af702010-06-04 16:37:43 -04005893 if (mSelectingText) {
5894 // tapping on selection or controls does nothing
5895 if (!nativeHitSelection(contentX, contentY)) {
5896 selectionDone();
5897 }
5898 break;
5899 }
Grace Klobaec9a1042010-06-01 19:00:23 -07005900 // only trigger double tap if the WebView is
5901 // scalable
5902 if (mTouchMode == TOUCH_INIT_MODE
5903 && (canZoomIn() || canZoomOut())) {
Grace Klobac2242f22010-03-05 14:00:26 -08005904 mPrivateHandler.sendEmptyMessageDelayed(
5905 RELEASE_SINGLE_TAP, ViewConfiguration
5906 .getDoubleTapTimeout());
5907 } else {
5908 doShortPress();
Grace Klobaf58af622009-09-24 17:41:23 -07005909 }
5910 break;
5911 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005912 case TOUCH_DRAG_MODE:
Patrick Scott2f492272010-12-03 09:52:35 -05005913 case TOUCH_DRAG_LAYER_MODE:
Cary Clark25415e22009-10-12 13:41:28 -04005914 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
5915 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005916 // if the user waits a while w/o moving before the
5917 // up, we don't want to do a fling
5918 if (eventTime - mLastTouchTime <= MIN_FLING_TIME) {
Grace Klobac2242f22010-03-05 14:00:26 -08005919 if (mVelocityTracker == null) {
5920 Log.e(LOGTAG, "Got null mVelocityTracker when "
5921 + "mPreventDefault = "
5922 + mPreventDefault
5923 + " mDeferTouchProcess = "
5924 + mDeferTouchProcess);
Huahui Wu8465cc92011-01-12 10:17:19 -08005925 } else {
5926 mVelocityTracker.addMovement(ev);
Grace Klobac2242f22010-03-05 14:00:26 -08005927 }
Grace Kloba9b657802010-04-08 13:46:23 -07005928 // set to MOTIONLESS_IGNORE so that it won't keep
5929 // removing and sending message in
5930 // drawCoreAndCursorRing()
5931 mHeldMotionless = MOTIONLESS_IGNORE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005932 doFling();
5933 break;
Adam Powell637d3372010-08-25 14:37:03 -07005934 } else {
5935 if (mScroller.springBack(mScrollX, mScrollY, 0,
5936 computeMaxScrollX(), 0,
5937 computeMaxScrollY())) {
5938 invalidate();
5939 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005940 }
Grace Kloba9b657802010-04-08 13:46:23 -07005941 // redraw in high-quality, as we're done dragging
5942 mHeldMotionless = MOTIONLESS_TRUE;
5943 invalidate();
Grace Kloba524aab572010-04-07 11:12:52 -07005944 // fall through
5945 case TOUCH_DRAG_START_MODE:
5946 // TOUCH_DRAG_START_MODE should not happen for the real
5947 // device as we almost certain will get a MOVE. But this
5948 // is possible on emulator.
Cary Clark278ce052009-08-31 16:08:42 -04005949 mLastVelocity = 0;
Grace Klobaa7bc87c2010-01-29 14:56:25 -08005950 WebViewCore.resumePriority();
Cary Clark0df02692010-11-24 11:01:37 -05005951 if (!mSelectingText) {
5952 WebViewCore.resumeUpdatePicture(mWebViewCore);
5953 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005954 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005955 }
Grace Klobac2242f22010-03-05 14:00:26 -08005956 stopTouch();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005957 break;
5958 }
5959 case MotionEvent.ACTION_CANCEL: {
Grace Klobad7625dd2010-03-04 11:46:12 -08005960 if (mTouchMode == TOUCH_DRAG_MODE) {
Adam Powell637d3372010-08-25 14:37:03 -07005961 mScroller.springBack(mScrollX, mScrollY, 0,
5962 computeMaxScrollX(), 0, computeMaxScrollY());
Grace Klobac2242f22010-03-05 14:00:26 -08005963 invalidate();
Grace Klobad7625dd2010-03-04 11:46:12 -08005964 }
Grace Klobac2242f22010-03-05 14:00:26 -08005965 cancelWebCoreTouchEvent(contentX, contentY, false);
5966 cancelTouch();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08005967 break;
5968 }
5969 }
5970 return true;
5971 }
Cary Clarkd6982c92009-05-29 11:02:22 -04005972
Adam Powell4fb35d42011-03-03 17:54:55 -08005973 private void passMultiTouchToWebKit(MotionEvent ev, long sequence) {
Huahui Wu41865f42010-09-02 13:41:41 -07005974 TouchEventData ted = new TouchEventData();
Huahui Wu1f301252011-01-19 17:32:32 -08005975 ted.mAction = ev.getActionMasked();
Huahui Wu41865f42010-09-02 13:41:41 -07005976 final int count = ev.getPointerCount();
Huahui Wue838a422011-01-13 16:03:43 -08005977 ted.mIds = new int[count];
Huahui Wu41865f42010-09-02 13:41:41 -07005978 ted.mPoints = new Point[count];
Huahui Wu88b869a2011-03-17 17:42:12 -07005979 ted.mPointsInView = new Point[count];
Huahui Wu41865f42010-09-02 13:41:41 -07005980 for (int c = 0; c < count; c++) {
Huahui Wue838a422011-01-13 16:03:43 -08005981 ted.mIds[c] = ev.getPointerId(c);
Huahui Wu41865f42010-09-02 13:41:41 -07005982 int x = viewToContentX((int) ev.getX(c) + mScrollX);
5983 int y = viewToContentY((int) ev.getY(c) + mScrollY);
5984 ted.mPoints[c] = new Point(x, y);
Huahui Wu88b869a2011-03-17 17:42:12 -07005985 ted.mPointsInView[c] = new Point((int) ev.getX(c), (int) ev.getY(c));
Huahui Wu41865f42010-09-02 13:41:41 -07005986 }
Huahui Wu2d3ef372011-03-13 18:13:42 -07005987 if (ted.mAction == MotionEvent.ACTION_POINTER_DOWN
5988 || ted.mAction == MotionEvent.ACTION_POINTER_UP) {
5989 ted.mActionIndex = ev.getActionIndex();
5990 }
Huahui Wu41865f42010-09-02 13:41:41 -07005991 ted.mMetaState = ev.getMetaState();
Huahui Wu0904c0d2011-01-07 17:30:53 -08005992 ted.mReprocess = true;
5993 ted.mMotionEvent = MotionEvent.obtain(ev);
Adam Powell4fb35d42011-03-03 17:54:55 -08005994 ted.mSequence = sequence;
Adam Powell3c534772011-04-04 14:27:12 -07005995 mTouchEventQueue.preQueueTouchEventData(ted);
Huahui Wu41865f42010-09-02 13:41:41 -07005996 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
5997 cancelLongPress();
5998 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
Huahui Wu41865f42010-09-02 13:41:41 -07005999 }
6000
Adam Powell8f626a12011-03-10 19:27:15 -08006001 void handleMultiTouchInWebView(MotionEvent ev) {
Huahui Wu0904c0d2011-01-07 17:30:53 -08006002 if (DebugFlags.WEB_VIEW) {
6003 Log.v(LOGTAG, "multi-touch: " + ev + " at " + ev.getEventTime()
6004 + " mTouchMode=" + mTouchMode
6005 + " numPointers=" + ev.getPointerCount()
6006 + " scrolloffset=(" + mScrollX + "," + mScrollY + ")");
6007 }
6008
6009 final ScaleGestureDetector detector =
6010 mZoomManager.getMultiTouchGestureDetector();
Huahui Wu8465cc92011-01-12 10:17:19 -08006011
6012 // A few apps use WebView but don't instantiate gesture detector.
6013 // We don't need to support multi touch for them.
Huahui Wu1f301252011-01-19 17:32:32 -08006014 if (detector == null) return;
Huahui Wu8465cc92011-01-12 10:17:19 -08006015
Huahui Wu0904c0d2011-01-07 17:30:53 -08006016 float x = ev.getX();
6017 float y = ev.getY();
Huahui Wu0904c0d2011-01-07 17:30:53 -08006018
Adam Powell8f626a12011-03-10 19:27:15 -08006019 if (mPreventDefault != PREVENT_DEFAULT_YES) {
6020 detector.onTouchEvent(ev);
Huahui Wu0904c0d2011-01-07 17:30:53 -08006021
Adam Powell8f626a12011-03-10 19:27:15 -08006022 if (detector.isInProgress()) {
6023 if (DebugFlags.WEB_VIEW) {
6024 Log.v(LOGTAG, "detector is in progress");
6025 }
6026 mLastTouchTime = ev.getEventTime();
6027 x = detector.getFocusX();
6028 y = detector.getFocusY();
Huahui Wuf147d452011-01-12 14:02:36 -08006029
Adam Powell8f626a12011-03-10 19:27:15 -08006030 cancelLongPress();
6031 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
6032 if (!mZoomManager.supportsPanDuringZoom()) {
6033 return;
6034 }
6035 mTouchMode = TOUCH_DRAG_MODE;
6036 if (mVelocityTracker == null) {
6037 mVelocityTracker = VelocityTracker.obtain();
6038 }
Huahui Wu0904c0d2011-01-07 17:30:53 -08006039 }
6040 }
6041
Huahui Wuf147d452011-01-12 14:02:36 -08006042 int action = ev.getActionMasked();
Huahui Wu0904c0d2011-01-07 17:30:53 -08006043 if (action == MotionEvent.ACTION_POINTER_DOWN) {
6044 cancelTouch();
6045 action = MotionEvent.ACTION_DOWN;
Adam Powell4fb35d42011-03-03 17:54:55 -08006046 } else if (action == MotionEvent.ACTION_POINTER_UP && ev.getPointerCount() == 2) {
Huahui Wu0904c0d2011-01-07 17:30:53 -08006047 // set mLastTouchX/Y to the remaining point
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08006048 mLastTouchX = Math.round(x);
6049 mLastTouchY = Math.round(y);
Huahui Wu0904c0d2011-01-07 17:30:53 -08006050 } else if (action == MotionEvent.ACTION_MOVE) {
6051 // negative x or y indicate it is on the edge, skip it.
6052 if (x < 0 || y < 0) {
Huahui Wu1f301252011-01-19 17:32:32 -08006053 return;
Huahui Wu0904c0d2011-01-07 17:30:53 -08006054 }
6055 }
6056
Huahui Wu1f301252011-01-19 17:32:32 -08006057 handleTouchEventCommon(ev, action, Math.round(x), Math.round(y));
Huahui Wu0904c0d2011-01-07 17:30:53 -08006058 }
6059
Grace Klobac2242f22010-03-05 14:00:26 -08006060 private void cancelWebCoreTouchEvent(int x, int y, boolean removeEvents) {
6061 if (shouldForwardTouchEvent()) {
6062 if (removeEvents) {
6063 mWebViewCore.removeMessages(EventHub.TOUCH_EVENT);
6064 }
6065 TouchEventData ted = new TouchEventData();
Huahui Wue838a422011-01-13 16:03:43 -08006066 ted.mIds = new int[1];
6067 ted.mIds[0] = 0;
Huahui Wu41865f42010-09-02 13:41:41 -07006068 ted.mPoints = new Point[1];
6069 ted.mPoints[0] = new Point(x, y);
Huahui Wu88b869a2011-03-17 17:42:12 -07006070 ted.mPointsInView = new Point[1];
6071 int viewX = contentToViewX(x) - mScrollX;
6072 int viewY = contentToViewY(y) - mScrollY;
6073 ted.mPointsInView[0] = new Point(viewX, viewY);
Grace Klobac2242f22010-03-05 14:00:26 -08006074 ted.mAction = MotionEvent.ACTION_CANCEL;
Patrick Scottcfa734a2011-02-22 11:19:02 -05006075 ted.mNativeLayer = nativeScrollableLayer(
6076 x, y, ted.mNativeLayerRect, null);
Adam Powellae9d2642011-03-08 16:00:30 -08006077 ted.mSequence = mTouchEventQueue.nextTouchSequence();
Grace Klobac2242f22010-03-05 14:00:26 -08006078 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
6079 mPreventDefault = PREVENT_DEFAULT_IGNORE;
Adam Powellbaa33802011-03-25 13:58:19 -07006080
6081 if (removeEvents) {
6082 // Mark this after sending the message above; we should
6083 // be willing to ignore the cancel event that we just sent.
6084 mTouchEventQueue.ignoreCurrentlyMissingEvents();
6085 }
Grace Klobac2242f22010-03-05 14:00:26 -08006086 }
6087 }
6088
Grace Kloba3a0def22010-01-23 21:11:54 -08006089 private void startTouch(float x, float y, long eventTime) {
6090 // Remember where the motion event started
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08006091 mLastTouchX = Math.round(x);
6092 mLastTouchY = Math.round(y);
Grace Kloba3a0def22010-01-23 21:11:54 -08006093 mLastTouchTime = eventTime;
6094 mVelocityTracker = VelocityTracker.obtain();
6095 mSnapScrollMode = SNAP_NONE;
Grace Kloba3a0def22010-01-23 21:11:54 -08006096 }
6097
Grace Klobac2242f22010-03-05 14:00:26 -08006098 private void startDrag() {
6099 WebViewCore.reducePriority();
Grace Kloba524aab572010-04-07 11:12:52 -07006100 // to get better performance, pause updating the picture
6101 WebViewCore.pauseUpdatePicture(mWebViewCore);
Grace Klobac2242f22010-03-05 14:00:26 -08006102 if (!mDragFromTextInput) {
6103 nativeHideCursor();
6104 }
Derek Sollenberger90b6e482010-05-10 12:38:54 -04006105
6106 if (mHorizontalScrollBarMode != SCROLLBAR_ALWAYSOFF
6107 || mVerticalScrollBarMode != SCROLLBAR_ALWAYSOFF) {
6108 mZoomManager.invokeZoomPicker();
Grace Klobac2242f22010-03-05 14:00:26 -08006109 }
6110 }
6111
6112 private void doDrag(int deltaX, int deltaY) {
6113 if ((deltaX | deltaY) != 0) {
Patrick Scott62310912010-12-06 17:44:50 -05006114 int oldX = mScrollX;
6115 int oldY = mScrollY;
6116 int rangeX = computeMaxScrollX();
6117 int rangeY = computeMaxScrollY();
6118 int overscrollDistance = mOverscrollDistance;
Adam Powell637d3372010-08-25 14:37:03 -07006119
Patrick Scott62310912010-12-06 17:44:50 -05006120 // Check for the original scrolling layer in case we change
6121 // directions. mTouchMode might be TOUCH_DRAG_MODE if we have
6122 // reached the edge of a layer but mScrollingLayer will be non-zero
6123 // if we initiated the drag on a layer.
6124 if (mScrollingLayer != 0) {
6125 final int contentX = viewToContentDimension(deltaX);
6126 final int contentY = viewToContentDimension(deltaY);
6127
6128 // Check the scrolling bounds to see if we will actually do any
6129 // scrolling. The rectangle is in document coordinates.
6130 final int maxX = mScrollingLayerRect.right;
6131 final int maxY = mScrollingLayerRect.bottom;
6132 final int resultX = Math.max(0,
6133 Math.min(mScrollingLayerRect.left + contentX, maxX));
6134 final int resultY = Math.max(0,
6135 Math.min(mScrollingLayerRect.top + contentY, maxY));
6136
6137 if (resultX != mScrollingLayerRect.left ||
6138 resultY != mScrollingLayerRect.top) {
6139 // In case we switched to dragging the page.
6140 mTouchMode = TOUCH_DRAG_LAYER_MODE;
6141 deltaX = contentX;
6142 deltaY = contentY;
6143 oldX = mScrollingLayerRect.left;
6144 oldY = mScrollingLayerRect.top;
6145 rangeX = maxX;
6146 rangeY = maxY;
6147 } else {
6148 // Scroll the main page if we are not going to scroll the
6149 // layer. This does not reset mScrollingLayer in case the
6150 // user changes directions and the layer can scroll the
6151 // other way.
6152 mTouchMode = TOUCH_DRAG_MODE;
6153 }
6154 }
Adam Powell637d3372010-08-25 14:37:03 -07006155
6156 if (mOverScrollGlow != null) {
6157 mOverScrollGlow.setOverScrollDeltas(deltaX, deltaY);
6158 }
6159
6160 overScrollBy(deltaX, deltaY, oldX, oldY,
6161 rangeX, rangeY,
6162 mOverscrollDistance, mOverscrollDistance, true);
6163 if (mOverScrollGlow != null && mOverScrollGlow.isAnimating()) {
6164 invalidate();
6165 }
Grace Klobac2242f22010-03-05 14:00:26 -08006166 }
Derek Sollenberger90b6e482010-05-10 12:38:54 -04006167 mZoomManager.keepZoomPickerVisible();
Grace Klobac2242f22010-03-05 14:00:26 -08006168 }
6169
6170 private void stopTouch() {
Grace Klobac2242f22010-03-05 14:00:26 -08006171 // we also use mVelocityTracker == null to tell us that we are
6172 // not "moving around", so we can take the slower/prettier
6173 // mode in the drawing code
6174 if (mVelocityTracker != null) {
6175 mVelocityTracker.recycle();
6176 mVelocityTracker = null;
6177 }
Adam Powell637d3372010-08-25 14:37:03 -07006178
6179 // Release any pulled glows
6180 if (mOverScrollGlow != null) {
6181 mOverScrollGlow.releaseAll();
6182 }
Grace Klobac2242f22010-03-05 14:00:26 -08006183 }
6184
Grace Kloba3a0def22010-01-23 21:11:54 -08006185 private void cancelTouch() {
Grace Kloba3a0def22010-01-23 21:11:54 -08006186 // we also use mVelocityTracker == null to tell us that we are
6187 // not "moving around", so we can take the slower/prettier
6188 // mode in the drawing code
6189 if (mVelocityTracker != null) {
6190 mVelocityTracker.recycle();
6191 mVelocityTracker = null;
6192 }
Adam Powell637d3372010-08-25 14:37:03 -07006193
Cary Clark0df02692010-11-24 11:01:37 -05006194 if ((mTouchMode == TOUCH_DRAG_MODE
6195 || mTouchMode == TOUCH_DRAG_LAYER_MODE) && !mSelectingText) {
Grace Klobaa7bc87c2010-01-29 14:56:25 -08006196 WebViewCore.resumePriority();
Grace Kloba524aab572010-04-07 11:12:52 -07006197 WebViewCore.resumeUpdatePicture(mWebViewCore);
Grace Kloba3a0def22010-01-23 21:11:54 -08006198 }
6199 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
6200 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
6201 mPrivateHandler.removeMessages(DRAG_HELD_MOTIONLESS);
6202 mPrivateHandler.removeMessages(AWAKEN_SCROLL_BARS);
Grace Kloba178db412010-05-18 22:22:23 -07006203 if (getSettings().supportTouchOnly()) {
6204 removeTouchHighlight(true);
6205 }
Grace Kloba3a0def22010-01-23 21:11:54 -08006206 mHeldMotionless = MOTIONLESS_TRUE;
6207 mTouchMode = TOUCH_DONE_MODE;
6208 nativeHideCursor();
6209 }
6210
Jeff Brown33bbfd22011-02-24 20:55:35 -08006211 @Override
6212 public boolean onGenericMotionEvent(MotionEvent event) {
6213 if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) != 0) {
6214 switch (event.getAction()) {
6215 case MotionEvent.ACTION_SCROLL: {
6216 final float vscroll;
6217 final float hscroll;
6218 if ((event.getMetaState() & KeyEvent.META_SHIFT_ON) != 0) {
6219 vscroll = 0;
6220 hscroll = event.getAxisValue(MotionEvent.AXIS_VSCROLL);
6221 } else {
6222 vscroll = -event.getAxisValue(MotionEvent.AXIS_VSCROLL);
6223 hscroll = event.getAxisValue(MotionEvent.AXIS_HSCROLL);
6224 }
6225 if (hscroll != 0 || vscroll != 0) {
6226 final int vdelta = (int) (vscroll * getVerticalScrollFactor());
6227 final int hdelta = (int) (hscroll * getHorizontalScrollFactor());
6228 if (pinScrollBy(hdelta, vdelta, true, 0)) {
6229 return true;
6230 }
6231 }
6232 }
6233 }
6234 }
6235 return super.onGenericMotionEvent(event);
6236 }
6237
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006238 private long mTrackballFirstTime = 0;
6239 private long mTrackballLastTime = 0;
6240 private float mTrackballRemainsX = 0.0f;
6241 private float mTrackballRemainsY = 0.0f;
6242 private int mTrackballXMove = 0;
6243 private int mTrackballYMove = 0;
Cary Clark924af702010-06-04 16:37:43 -04006244 private boolean mSelectingText = false;
6245 private boolean mSelectionStarted = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006246 private boolean mExtendSelection = false;
Cary Clark924af702010-06-04 16:37:43 -04006247 private boolean mDrawSelectionPointer = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006248 private static final int TRACKBALL_KEY_TIMEOUT = 1000;
6249 private static final int TRACKBALL_TIMEOUT = 200;
6250 private static final int TRACKBALL_WAIT = 100;
6251 private static final int TRACKBALL_SCALE = 400;
6252 private static final int TRACKBALL_SCROLL_COUNT = 5;
6253 private static final int TRACKBALL_MOVE_COUNT = 10;
6254 private static final int TRACKBALL_MULTIPLIER = 3;
6255 private static final int SELECT_CURSOR_OFFSET = 16;
Cary Clark6f5dfc62010-11-11 13:09:20 -05006256 private static final int SELECT_SCROLL = 5;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006257 private int mSelectX = 0;
6258 private int mSelectY = 0;
Cary Clark5da9aeb2009-10-06 17:40:53 -04006259 private boolean mFocusSizeChanged = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006260 private boolean mTrackballDown = false;
6261 private long mTrackballUpTime = 0;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006262 private long mLastCursorTime = 0;
6263 private Rect mLastCursorBounds;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006264
6265 // Set by default; BrowserActivity clears to interpret trackball data
Cary Clarkd6982c92009-05-29 11:02:22 -04006266 // directly for movement. Currently, the framework only passes
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006267 // arrow key events, not trackball events, from one child to the next
6268 private boolean mMapTrackballToArrowKeys = true;
Cary Clarkd6982c92009-05-29 11:02:22 -04006269
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006270 public void setMapTrackballToArrowKeys(boolean setMap) {
6271 mMapTrackballToArrowKeys = setMap;
6272 }
6273
6274 void resetTrackballTime() {
6275 mTrackballLastTime = 0;
6276 }
6277
6278 @Override
6279 public boolean onTrackballEvent(MotionEvent ev) {
6280 long time = ev.getEventTime();
6281 if ((ev.getMetaState() & KeyEvent.META_ALT_ON) != 0) {
6282 if (ev.getY() > 0) pageDown(true);
6283 if (ev.getY() < 0) pageUp(true);
6284 return true;
6285 }
6286 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
Cary Clark924af702010-06-04 16:37:43 -04006287 if (mSelectingText) {
Cary Clarkbadd8392009-10-15 13:32:08 -04006288 return true; // discard press if copy in progress
6289 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006290 mTrackballDown = true;
Leon Scroggins3ccd3652009-06-26 17:22:50 -04006291 if (mNativeClass == 0) {
6292 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006293 }
Leon Scroggins3ccd3652009-06-26 17:22:50 -04006294 nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006295 if (time - mLastCursorTime <= TRACKBALL_TIMEOUT
6296 && !mLastCursorBounds.equals(nativeGetCursorRingBounds())) {
6297 nativeSelectBestAt(mLastCursorBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006298 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006299 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006300 Log.v(LOGTAG, "onTrackballEvent down ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04006301 + " time=" + time
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04006302 + " mLastCursorTime=" + mLastCursorTime);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006303 }
6304 if (isInTouchMode()) requestFocusFromTouch();
6305 return false; // let common code in onKeyDown at it
Cary Clarkd6982c92009-05-29 11:02:22 -04006306 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006307 if (ev.getAction() == MotionEvent.ACTION_UP) {
Leon Scrogginse3225672009-06-03 15:53:13 -04006308 // LONG_PRESS_CENTER is set in common onKeyDown
6309 mPrivateHandler.removeMessages(LONG_PRESS_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006310 mTrackballDown = false;
6311 mTrackballUpTime = time;
Cary Clark924af702010-06-04 16:37:43 -04006312 if (mSelectingText) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006313 if (mExtendSelection) {
Cary Clark924af702010-06-04 16:37:43 -04006314 copySelection();
6315 selectionDone();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006316 } else {
6317 mExtendSelection = true;
Cary Clark924af702010-06-04 16:37:43 -04006318 nativeSetExtendSelection();
Cary Clark09e383c2009-10-26 16:43:58 -04006319 invalidate(); // draw the i-beam instead of the arrow
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006320 }
Cary Clarkbadd8392009-10-15 13:32:08 -04006321 return true; // discard press if copy in progress
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006322 }
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006323 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006324 Log.v(LOGTAG, "onTrackballEvent up ev=" + ev
Cary Clarkd6982c92009-05-29 11:02:22 -04006325 + " time=" + time
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006326 );
6327 }
6328 return false; // let common code in onKeyUp at it
6329 }
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07006330 if ((mMapTrackballToArrowKeys && (ev.getMetaState() & KeyEvent.META_SHIFT_ON) == 0) ||
Svetoslav Ganov12bed782011-01-03 14:14:50 -08006331 AccessibilityManager.getInstance(mContext).isEnabled()) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006332 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent gmail quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006333 return false;
6334 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006335 if (mTrackballDown) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006336 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent down quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006337 return true; // discard move if trackball is down
6338 }
6339 if (time - mTrackballUpTime < TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006340 if (DebugFlags.WEB_VIEW) Log.v(LOGTAG, "onTrackballEvent up timeout quit");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006341 return true;
6342 }
6343 // TODO: alternatively we can do panning as touch does
6344 switchOutDrawHistory();
6345 if (time - mTrackballLastTime > TRACKBALL_TIMEOUT) {
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006346 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006347 Log.v(LOGTAG, "onTrackballEvent time="
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006348 + time + " last=" + mTrackballLastTime);
6349 }
6350 mTrackballFirstTime = time;
6351 mTrackballXMove = mTrackballYMove = 0;
6352 }
6353 mTrackballLastTime = time;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006354 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006355 Log.v(LOGTAG, "onTrackballEvent ev=" + ev + " time=" + time);
6356 }
6357 mTrackballRemainsX += ev.getX();
6358 mTrackballRemainsY += ev.getY();
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07006359 doTrackball(time, ev.getMetaState());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006360 return true;
6361 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006363 void moveSelection(float xRate, float yRate) {
6364 if (mNativeClass == 0)
6365 return;
6366 int width = getViewWidth();
6367 int height = getViewHeight();
Cary Clarkc05af372009-10-16 10:52:27 -04006368 mSelectX += xRate;
6369 mSelectY += yRate;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006370 int maxX = width + mScrollX;
6371 int maxY = height + mScrollY;
6372 mSelectX = Math.min(maxX, Math.max(mScrollX - SELECT_CURSOR_OFFSET
6373 , mSelectX));
6374 mSelectY = Math.min(maxY, Math.max(mScrollY - SELECT_CURSOR_OFFSET
6375 , mSelectY));
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006376 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006377 Log.v(LOGTAG, "moveSelection"
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006378 + " mSelectX=" + mSelectX
6379 + " mSelectY=" + mSelectY
6380 + " mScrollX=" + mScrollX
6381 + " mScrollY=" + mScrollY
6382 + " xRate=" + xRate
6383 + " yRate=" + yRate
6384 );
6385 }
Cary Clark924af702010-06-04 16:37:43 -04006386 nativeMoveSelection(viewToContentX(mSelectX), viewToContentY(mSelectY));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006387 int scrollX = mSelectX < mScrollX ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04006388 : mSelectX > maxX - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006389 : 0;
6390 int scrollY = mSelectY < mScrollY ? -SELECT_CURSOR_OFFSET
Cary Clarkd6982c92009-05-29 11:02:22 -04006391 : mSelectY > maxY - SELECT_CURSOR_OFFSET ? SELECT_CURSOR_OFFSET
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006392 : 0;
6393 pinScrollBy(scrollX, scrollY, true, 0);
6394 Rect select = new Rect(mSelectX, mSelectY, mSelectX + 1, mSelectY + 1);
6395 requestRectangleOnScreen(select);
6396 invalidate();
6397 }
6398
6399 private int scaleTrackballX(float xRate, int width) {
6400 int xMove = (int) (xRate / TRACKBALL_SCALE * width);
6401 int nextXMove = xMove;
6402 if (xMove > 0) {
6403 if (xMove > mTrackballXMove) {
6404 xMove -= mTrackballXMove;
6405 }
6406 } else if (xMove < mTrackballXMove) {
6407 xMove -= mTrackballXMove;
6408 }
6409 mTrackballXMove = nextXMove;
6410 return xMove;
6411 }
6412
6413 private int scaleTrackballY(float yRate, int height) {
6414 int yMove = (int) (yRate / TRACKBALL_SCALE * height);
6415 int nextYMove = yMove;
6416 if (yMove > 0) {
6417 if (yMove > mTrackballYMove) {
6418 yMove -= mTrackballYMove;
6419 }
6420 } else if (yMove < mTrackballYMove) {
6421 yMove -= mTrackballYMove;
6422 }
6423 mTrackballYMove = nextYMove;
6424 return yMove;
6425 }
6426
6427 private int keyCodeToSoundsEffect(int keyCode) {
6428 switch(keyCode) {
6429 case KeyEvent.KEYCODE_DPAD_UP:
6430 return SoundEffectConstants.NAVIGATION_UP;
6431 case KeyEvent.KEYCODE_DPAD_RIGHT:
6432 return SoundEffectConstants.NAVIGATION_RIGHT;
6433 case KeyEvent.KEYCODE_DPAD_DOWN:
6434 return SoundEffectConstants.NAVIGATION_DOWN;
6435 case KeyEvent.KEYCODE_DPAD_LEFT:
6436 return SoundEffectConstants.NAVIGATION_LEFT;
6437 }
6438 throw new IllegalArgumentException("keyCode must be one of " +
6439 "{KEYCODE_DPAD_UP, KEYCODE_DPAD_RIGHT, KEYCODE_DPAD_DOWN, " +
6440 "KEYCODE_DPAD_LEFT}.");
6441 }
6442
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07006443 private void doTrackball(long time, int metaState) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006444 int elapsed = (int) (mTrackballLastTime - mTrackballFirstTime);
6445 if (elapsed == 0) {
6446 elapsed = TRACKBALL_TIMEOUT;
6447 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006448 float xRate = mTrackballRemainsX * 1000 / elapsed;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006449 float yRate = mTrackballRemainsY * 1000 / elapsed;
Cary Clarkc05af372009-10-16 10:52:27 -04006450 int viewWidth = getViewWidth();
6451 int viewHeight = getViewHeight();
Cary Clark924af702010-06-04 16:37:43 -04006452 if (mSelectingText) {
6453 if (!mDrawSelectionPointer) {
6454 // The last selection was made by touch, disabling drawing the
6455 // selection pointer. Allow the trackball to adjust the
6456 // position of the touch control.
6457 mSelectX = contentToViewX(nativeSelectionX());
6458 mSelectY = contentToViewY(nativeSelectionY());
6459 mDrawSelectionPointer = mExtendSelection = true;
6460 nativeSetExtendSelection();
6461 }
Cary Clarkc05af372009-10-16 10:52:27 -04006462 moveSelection(scaleTrackballX(xRate, viewWidth),
6463 scaleTrackballY(yRate, viewHeight));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006464 mTrackballRemainsX = mTrackballRemainsY = 0;
6465 return;
6466 }
6467 float ax = Math.abs(xRate);
6468 float ay = Math.abs(yRate);
6469 float maxA = Math.max(ax, ay);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006470 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006471 Log.v(LOGTAG, "doTrackball elapsed=" + elapsed
6472 + " xRate=" + xRate
6473 + " yRate=" + yRate
6474 + " mTrackballRemainsX=" + mTrackballRemainsX
6475 + " mTrackballRemainsY=" + mTrackballRemainsY);
6476 }
Cary Clarkc05af372009-10-16 10:52:27 -04006477 int width = mContentWidth - viewWidth;
6478 int height = mContentHeight - viewHeight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006479 if (width < 0) width = 0;
6480 if (height < 0) height = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006481 ax = Math.abs(mTrackballRemainsX * TRACKBALL_MULTIPLIER);
6482 ay = Math.abs(mTrackballRemainsY * TRACKBALL_MULTIPLIER);
6483 maxA = Math.max(ax, ay);
6484 int count = Math.max(0, (int) maxA);
6485 int oldScrollX = mScrollX;
6486 int oldScrollY = mScrollY;
6487 if (count > 0) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006488 int selectKeyCode = ax < ay ? mTrackballRemainsY < 0 ?
6489 KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN :
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006490 mTrackballRemainsX < 0 ? KeyEvent.KEYCODE_DPAD_LEFT :
6491 KeyEvent.KEYCODE_DPAD_RIGHT;
6492 count = Math.min(count, TRACKBALL_MOVE_COUNT);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006493 if (DebugFlags.WEB_VIEW) {
Cary Clarkd6982c92009-05-29 11:02:22 -04006494 Log.v(LOGTAG, "doTrackball keyCode=" + selectKeyCode
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006495 + " count=" + count
6496 + " mTrackballRemainsX=" + mTrackballRemainsX
6497 + " mTrackballRemainsY=" + mTrackballRemainsY);
6498 }
Leon Scroggins9ab32b62010-05-03 14:19:50 +01006499 if (mNativeClass != 0 && nativePageShouldHandleShiftAndArrows()) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05006500 for (int i = 0; i < count; i++) {
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07006501 letPageHandleNavKey(selectKeyCode, time, true, metaState);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05006502 }
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07006503 letPageHandleNavKey(selectKeyCode, time, false, metaState);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05006504 } else if (navHandledKey(selectKeyCode, count, false, time)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006505 playSoundEffect(keyCodeToSoundsEffect(selectKeyCode));
6506 }
6507 mTrackballRemainsX = mTrackballRemainsY = 0;
6508 }
6509 if (count >= TRACKBALL_SCROLL_COUNT) {
6510 int xMove = scaleTrackballX(xRate, width);
6511 int yMove = scaleTrackballY(yRate, height);
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04006512 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006513 Log.v(LOGTAG, "doTrackball pinScrollBy"
6514 + " count=" + count
6515 + " xMove=" + xMove + " yMove=" + yMove
Cary Clarkd6982c92009-05-29 11:02:22 -04006516 + " mScrollX-oldScrollX=" + (mScrollX-oldScrollX)
6517 + " mScrollY-oldScrollY=" + (mScrollY-oldScrollY)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006518 );
6519 }
6520 if (Math.abs(mScrollX - oldScrollX) > Math.abs(xMove)) {
6521 xMove = 0;
6522 }
6523 if (Math.abs(mScrollY - oldScrollY) > Math.abs(yMove)) {
6524 yMove = 0;
6525 }
6526 if (xMove != 0 || yMove != 0) {
6527 pinScrollBy(xMove, yMove, true, 0);
6528 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006529 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006530 }
6531
Adam Powell637d3372010-08-25 14:37:03 -07006532 /**
6533 * Compute the maximum horizontal scroll position. Used by {@link OverScrollGlow}.
6534 * @return Maximum horizontal scroll position within real content
6535 */
6536 int computeMaxScrollX() {
6537 return Math.max(computeRealHorizontalScrollRange() - getViewWidth(), 0);
Grace Klobad7625dd2010-03-04 11:46:12 -08006538 }
6539
Adam Powell637d3372010-08-25 14:37:03 -07006540 /**
6541 * Compute the maximum vertical scroll position. Used by {@link OverScrollGlow}.
6542 * @return Maximum vertical scroll position within real content
6543 */
6544 int computeMaxScrollY() {
6545 return Math.max(computeRealVerticalScrollRange() + getTitleHeight()
Mike Reede5e63f42010-03-19 14:38:23 -04006546 - getViewHeightWithTitle(), 0);
Mike Reede8853fc2009-09-04 14:01:48 -04006547 }
6548
Derek Sollenberger03e48912010-05-18 17:03:42 -04006549 boolean updateScrollCoordinates(int x, int y) {
6550 int oldX = mScrollX;
6551 int oldY = mScrollY;
6552 mScrollX = x;
6553 mScrollY = y;
6554 if (oldX != mScrollX || oldY != mScrollY) {
6555 onScrollChanged(mScrollX, mScrollY, oldX, oldY);
6556 return true;
6557 } else {
6558 return false;
6559 }
6560 }
6561
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006562 public void flingScroll(int vx, int vy) {
Grace Klobad7625dd2010-03-04 11:46:12 -08006563 mScroller.fling(mScrollX, mScrollY, vx, vy, 0, computeMaxScrollX(), 0,
Adam Powell637d3372010-08-25 14:37:03 -07006564 computeMaxScrollY(), mOverflingDistance, mOverflingDistance);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006565 invalidate();
6566 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006567
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006568 private void doFling() {
6569 if (mVelocityTracker == null) {
6570 return;
6571 }
Grace Klobad7625dd2010-03-04 11:46:12 -08006572 int maxX = computeMaxScrollX();
Mike Reede8853fc2009-09-04 14:01:48 -04006573 int maxY = computeMaxScrollY();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006574
Romain Guy4296fc42009-07-06 11:48:52 -07006575 mVelocityTracker.computeCurrentVelocity(1000, mMaximumFling);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006576 int vx = (int) mVelocityTracker.getXVelocity();
6577 int vy = (int) mVelocityTracker.getYVelocity();
6578
Patrick Scott62310912010-12-06 17:44:50 -05006579 int scrollX = mScrollX;
6580 int scrollY = mScrollY;
6581 int overscrollDistance = mOverscrollDistance;
6582 int overflingDistance = mOverflingDistance;
6583
6584 // Use the layer's scroll data if applicable.
6585 if (mTouchMode == TOUCH_DRAG_LAYER_MODE) {
6586 scrollX = mScrollingLayerRect.left;
6587 scrollY = mScrollingLayerRect.top;
6588 maxX = mScrollingLayerRect.right;
6589 maxY = mScrollingLayerRect.bottom;
6590 // No overscrolling for layers.
6591 overscrollDistance = overflingDistance = 0;
6592 }
6593
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006594 if (mSnapScrollMode != SNAP_NONE) {
Cary Clarkac492e12009-10-14 14:53:37 -04006595 if ((mSnapScrollMode & SNAP_X) == SNAP_X) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006596 vy = 0;
6597 } else {
6598 vx = 0;
6599 }
6600 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006601 if (true /* EMG release: make our fling more like Maps' */) {
6602 // maps cuts their velocity in half
6603 vx = vx * 3 / 4;
6604 vy = vy * 3 / 4;
6605 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04006606 if ((maxX == 0 && vy == 0) || (maxY == 0 && vx == 0)) {
Grace Klobaa7bc87c2010-01-29 14:56:25 -08006607 WebViewCore.resumePriority();
Cary Clark0df02692010-11-24 11:01:37 -05006608 if (!mSelectingText) {
6609 WebViewCore.resumeUpdatePicture(mWebViewCore);
6610 }
Patrick Scott62310912010-12-06 17:44:50 -05006611 if (mScroller.springBack(scrollX, scrollY, 0, maxX, 0, maxY)) {
Adam Powell637d3372010-08-25 14:37:03 -07006612 invalidate();
6613 }
Cary Clarkaa7caa62009-09-08 14:15:07 -04006614 return;
6615 }
Cary Clark278ce052009-08-31 16:08:42 -04006616 float currentVelocity = mScroller.getCurrVelocity();
Grace Kloba3c19d992010-05-17 19:19:06 -07006617 float velocity = (float) Math.hypot(vx, vy);
6618 if (mLastVelocity > 0 && currentVelocity > 0 && velocity
6619 > mLastVelocity * MINIMUM_VELOCITY_RATIO_FOR_ACCELERATION) {
Cary Clark278ce052009-08-31 16:08:42 -04006620 float deltaR = (float) (Math.abs(Math.atan2(mLastVelY, mLastVelX)
6621 - Math.atan2(vy, vx)));
6622 final float circle = (float) (Math.PI) * 2.0f;
6623 if (deltaR > circle * 0.9f || deltaR < circle * 0.1f) {
6624 vx += currentVelocity * mLastVelX / mLastVelocity;
6625 vy += currentVelocity * mLastVelY / mLastVelocity;
Grace Kloba3c19d992010-05-17 19:19:06 -07006626 velocity = (float) Math.hypot(vx, vy);
Cary Clark278ce052009-08-31 16:08:42 -04006627 if (DebugFlags.WEB_VIEW) {
6628 Log.v(LOGTAG, "doFling vx= " + vx + " vy=" + vy);
6629 }
6630 } else if (DebugFlags.WEB_VIEW) {
6631 Log.v(LOGTAG, "doFling missed " + deltaR / circle);
6632 }
6633 } else if (DebugFlags.WEB_VIEW) {
6634 Log.v(LOGTAG, "doFling start last=" + mLastVelocity
Cary Clarkaa7caa62009-09-08 14:15:07 -04006635 + " current=" + currentVelocity
6636 + " vx=" + vx + " vy=" + vy
6637 + " maxX=" + maxX + " maxY=" + maxY
Patrick Scott62310912010-12-06 17:44:50 -05006638 + " scrollX=" + scrollX + " scrollY=" + scrollY
6639 + " layer=" + mScrollingLayer);
Cary Clark278ce052009-08-31 16:08:42 -04006640 }
Adam Powell637d3372010-08-25 14:37:03 -07006641
6642 // Allow sloppy flings without overscrolling at the edges.
Patrick Scott62310912010-12-06 17:44:50 -05006643 if ((scrollX == 0 || scrollX == maxX) && Math.abs(vx) < Math.abs(vy)) {
Adam Powell637d3372010-08-25 14:37:03 -07006644 vx = 0;
6645 }
Patrick Scott62310912010-12-06 17:44:50 -05006646 if ((scrollY == 0 || scrollY == maxY) && Math.abs(vy) < Math.abs(vx)) {
Adam Powell637d3372010-08-25 14:37:03 -07006647 vy = 0;
6648 }
6649
Patrick Scott62310912010-12-06 17:44:50 -05006650 if (overscrollDistance < overflingDistance) {
6651 if ((vx > 0 && scrollX == -overscrollDistance) ||
6652 (vx < 0 && scrollX == maxX + overscrollDistance)) {
Adam Powell637d3372010-08-25 14:37:03 -07006653 vx = 0;
6654 }
Patrick Scott62310912010-12-06 17:44:50 -05006655 if ((vy > 0 && scrollY == -overscrollDistance) ||
6656 (vy < 0 && scrollY == maxY + overscrollDistance)) {
Adam Powell637d3372010-08-25 14:37:03 -07006657 vy = 0;
6658 }
6659 }
6660
Cary Clark278ce052009-08-31 16:08:42 -04006661 mLastVelX = vx;
6662 mLastVelY = vy;
Grace Kloba3c19d992010-05-17 19:19:06 -07006663 mLastVelocity = velocity;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006664
Adam Powell637d3372010-08-25 14:37:03 -07006665 // no horizontal overscroll if the content just fits
Patrick Scott62310912010-12-06 17:44:50 -05006666 mScroller.fling(scrollX, scrollY, -vx, -vy, 0, maxX, 0, maxY,
6667 maxX == 0 ? 0 : overflingDistance, overflingDistance);
Adam Powell637d3372010-08-25 14:37:03 -07006668 // Duration is calculated based on velocity. With range boundaries and overscroll
6669 // we may not know how long the final animation will take. (Hence the deprecation
6670 // warning on the call below.) It's not a big deal for scroll bars but if webcore
6671 // resumes during this effect we will take a performance hit. See computeScroll;
6672 // we resume webcore there when the animation is finished.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006673 final int time = mScroller.getDuration();
Patrick Scott5b87e292010-12-13 09:20:32 -05006674
6675 // Suppress scrollbars for layer scrolling.
6676 if (mTouchMode != TOUCH_DRAG_LAYER_MODE) {
6677 awakenScrollBars(time);
6678 }
6679
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006680 invalidate();
6681 }
6682
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07006683 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006684 * Returns a view containing zoom controls i.e. +/- buttons. The caller is
6685 * in charge of installing this view to the view hierarchy. This view will
6686 * become visible when the user starts scrolling via touch and fade away if
6687 * the user does not interact with it.
The Android Open Source Projectc39a6e02009-03-11 12:11:56 -07006688 * <p/>
The Android Open Source Project10592532009-03-18 17:39:46 -07006689 * API version 3 introduces a built-in zoom mechanism that is shown
6690 * automatically by the MapView. This is the preferred approach for
6691 * showing the zoom UI.
6692 *
6693 * @deprecated The built-in zoom mechanism is preferred, see
6694 * {@link WebSettings#setBuiltInZoomControls(boolean)}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006695 */
The Android Open Source Project10592532009-03-18 17:39:46 -07006696 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006697 public View getZoomControls() {
The Android Open Source Project10592532009-03-18 17:39:46 -07006698 if (!getSettings().supportZoom()) {
6699 Log.w(LOGTAG, "This WebView doesn't support zoom.");
6700 return null;
6701 }
Derek Sollenberger90b6e482010-05-10 12:38:54 -04006702 return mZoomManager.getExternalZoomPicker();
The Android Open Source Project10592532009-03-18 17:39:46 -07006703 }
6704
Derek Sollenberger90b6e482010-05-10 12:38:54 -04006705 void dismissZoomControl() {
6706 mZoomManager.dismissZoomPicker();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006707 }
6708
Derek Sollenberger03e48912010-05-18 17:03:42 -04006709 float getDefaultZoomScale() {
Derek Sollenberger341e22f2010-06-02 12:34:34 -04006710 return mZoomManager.getDefaultScale();
Derek Sollenberger03e48912010-05-18 17:03:42 -04006711 }
6712
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006713 /**
Grace Kloba6164ef12010-06-01 15:59:13 -07006714 * @return TRUE if the WebView can be zoomed in.
6715 */
6716 public boolean canZoomIn() {
6717 return mZoomManager.canZoomIn();
6718 }
6719
6720 /**
6721 * @return TRUE if the WebView can be zoomed out.
6722 */
6723 public boolean canZoomOut() {
6724 return mZoomManager.canZoomOut();
6725 }
6726
6727 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006728 * Perform zoom in in the webview
6729 * @return TRUE if zoom in succeeds. FALSE if no zoom changes.
6730 */
6731 public boolean zoomIn() {
Derek Sollenberger03e48912010-05-18 17:03:42 -04006732 return mZoomManager.zoomIn();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006733 }
6734
6735 /**
6736 * Perform zoom out in the webview
6737 * @return TRUE if zoom out succeeds. FALSE if no zoom changes.
6738 */
6739 public boolean zoomOut() {
Derek Sollenberger03e48912010-05-18 17:03:42 -04006740 return mZoomManager.zoomOut();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006741 }
6742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006743 private void updateSelection() {
6744 if (mNativeClass == 0) {
6745 return;
6746 }
6747 // mLastTouchX and mLastTouchY are the point in the current viewport
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08006748 int contentX = viewToContentX(mLastTouchX + mScrollX);
6749 int contentY = viewToContentY(mLastTouchY + mScrollY);
Cary Clarke9290822011-02-22 13:20:56 -05006750 int slop = viewToContentDimension(mNavSlop);
6751 Rect rect = new Rect(contentX - slop, contentY - slop,
6752 contentX + slop, contentY + slop);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006753 nativeSelectBestAt(rect);
Cary Clarkb8491342010-11-29 16:23:19 -05006754 mInitialHitTestResult = hitTestResult(null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006755 }
6756
Leon Scroggins0f5ad842009-07-17 15:08:34 -04006757 /**
Leon Scrogginsd5188652011-01-19 17:01:55 -05006758 * Scroll the focused text field to match the WebTextView
Cary Clarkeaa18de2009-09-28 12:50:42 -04006759 * @param xPercent New x position of the WebTextView from 0 to 1.
Leon Scroggins72543e12009-07-23 15:29:45 -04006760 */
Leon Scrogginsd5188652011-01-19 17:01:55 -05006761 /*package*/ void scrollFocusedTextInputX(float xPercent) {
Leon Scroggins72543e12009-07-23 15:29:45 -04006762 if (!inEditingMode() || mWebViewCore == null) {
6763 return;
6764 }
Leon Scrogginsd5188652011-01-19 17:01:55 -05006765 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0,
Cary Clarkeaa18de2009-09-28 12:50:42 -04006766 new Float(xPercent));
Leon Scroggins72543e12009-07-23 15:29:45 -04006767 }
6768
6769 /**
Leon Scrogginsd5188652011-01-19 17:01:55 -05006770 * Scroll the focused textarea vertically to match the WebTextView
6771 * @param y New y position of the WebTextView in view coordinates
6772 */
6773 /* package */ void scrollFocusedTextInputY(int y) {
Leon Scroggins22e883d2011-01-31 10:54:19 -05006774 if (!inEditingMode() || mWebViewCore == null) {
Leon Scrogginsd5188652011-01-19 17:01:55 -05006775 return;
6776 }
Leon Scroggins22e883d2011-01-31 10:54:19 -05006777 mWebViewCore.sendMessage(EventHub.SCROLL_TEXT_INPUT, 0, viewToContentDimension(y));
Leon Scrogginsd5188652011-01-19 17:01:55 -05006778 }
6779
6780 /**
Leon Scroggins0f5ad842009-07-17 15:08:34 -04006781 * Set our starting point and time for a drag from the WebTextView.
6782 */
6783 /*package*/ void initiateTextFieldDrag(float x, float y, long eventTime) {
6784 if (!inEditingMode()) {
6785 return;
6786 }
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08006787 mLastTouchX = Math.round(x + mWebTextView.getLeft() - mScrollX);
6788 mLastTouchY = Math.round(y + mWebTextView.getTop() - mScrollY);
Leon Scroggins0f5ad842009-07-17 15:08:34 -04006789 mLastTouchTime = eventTime;
6790 if (!mScroller.isFinished()) {
Cary Clark278ce052009-08-31 16:08:42 -04006791 abortAnimation();
Grace Kloba96949ef2010-01-25 09:53:01 -08006792 mPrivateHandler.removeMessages(RESUME_WEBCORE_PRIORITY);
Leon Scroggins0f5ad842009-07-17 15:08:34 -04006793 }
6794 mSnapScrollMode = SNAP_NONE;
6795 mVelocityTracker = VelocityTracker.obtain();
6796 mTouchMode = TOUCH_DRAG_START_MODE;
6797 }
6798
6799 /**
6800 * Given a motion event from the WebTextView, set its location to our
6801 * coordinates, and handle the event.
6802 */
6803 /*package*/ boolean textFieldDrag(MotionEvent event) {
6804 if (!inEditingMode()) {
6805 return false;
6806 }
Leon Scroggins72543e12009-07-23 15:29:45 -04006807 mDragFromTextInput = true;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04006808 event.offsetLocation((float) (mWebTextView.getLeft() - mScrollX),
6809 (float) (mWebTextView.getTop() - mScrollY));
Leon Scroggins72543e12009-07-23 15:29:45 -04006810 boolean result = onTouchEvent(event);
6811 mDragFromTextInput = false;
6812 return result;
Leon Scroggins0f5ad842009-07-17 15:08:34 -04006813 }
6814
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006815 /**
Leon Scrogginsf90b1262009-11-24 14:49:21 -05006816 * Due a touch up from a WebTextView. This will be handled by webkit to
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006817 * change the selection.
6818 * @param event MotionEvent in the WebTextView's coordinates.
6819 */
6820 /*package*/ void touchUpOnTextField(MotionEvent event) {
6821 if (!inEditingMode()) {
6822 return;
6823 }
Leon Scroggins0236e672009-09-02 21:12:08 -04006824 int x = viewToContentX((int) event.getX() + mWebTextView.getLeft());
6825 int y = viewToContentY((int) event.getY() + mWebTextView.getTop());
Cary Clarke9290822011-02-22 13:20:56 -05006826 int slop = viewToContentDimension(mNavSlop);
6827 nativeMotionUp(x, y, slop);
Leon Scroggins6679f2f2009-08-12 18:48:10 -04006828 }
6829
Leon Scroggins1d96ca02009-10-23 11:49:03 -04006830 /**
6831 * Called when pressing the center key or trackball on a textfield.
6832 */
6833 /*package*/ void centerKeyPressOnTextField() {
6834 mWebViewCore.sendMessage(EventHub.CLICK, nativeCursorFramePointer(),
6835 nativeCursorNodePointer());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006836 }
6837
6838 private void doShortPress() {
6839 if (mNativeClass == 0) {
6840 return;
6841 }
Grace Klobac2242f22010-03-05 14:00:26 -08006842 if (mPreventDefault == PREVENT_DEFAULT_YES) {
6843 return;
6844 }
6845 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006846 switchOutDrawHistory();
6847 // mLastTouchX and mLastTouchY are the point in the current viewport
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08006848 int contentX = viewToContentX(mLastTouchX + mScrollX);
6849 int contentY = viewToContentY(mLastTouchY + mScrollY);
Cary Clarke9290822011-02-22 13:20:56 -05006850 int slop = viewToContentDimension(mNavSlop);
Grace Kloba178db412010-05-18 22:22:23 -07006851 if (getSettings().supportTouchOnly()) {
6852 removeTouchHighlight(false);
6853 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
6854 // use "0" as generation id to inform WebKit to use the same x/y as
6855 // it used when processing GET_TOUCH_HIGHLIGHT_RECTS
6856 touchUpData.mMoveGeneration = 0;
6857 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
Cary Clarke9290822011-02-22 13:20:56 -05006858 } else if (nativePointInNavCache(contentX, contentY, slop)) {
Cary Clark1cb97ee2009-12-11 12:10:36 -05006859 WebViewCore.MotionUpData motionUpData = new WebViewCore
6860 .MotionUpData();
6861 motionUpData.mFrame = nativeCacheHitFramePointer();
6862 motionUpData.mNode = nativeCacheHitNodePointer();
6863 motionUpData.mBounds = nativeCacheHitNodeBounds();
6864 motionUpData.mX = contentX;
6865 motionUpData.mY = contentY;
6866 mWebViewCore.sendMessageAtFrontOfQueue(EventHub.VALID_NODE_BOUNDS,
6867 motionUpData);
6868 } else {
Cary Clarkbad0c542010-01-11 14:58:21 -05006869 doMotionUp(contentX, contentY);
Cary Clark1cb97ee2009-12-11 12:10:36 -05006870 }
6871 }
6872
Cary Clarkbad0c542010-01-11 14:58:21 -05006873 private void doMotionUp(int contentX, int contentY) {
Cary Clarke9290822011-02-22 13:20:56 -05006874 int slop = viewToContentDimension(mNavSlop);
6875 if (nativeMotionUp(contentX, contentY, slop) && mLogEvent) {
Dan Egnor18e93962010-02-10 19:27:58 -08006876 EventLog.writeEvent(EventLogTags.BROWSER_SNAP_CENTER);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006877 }
Cary Clarkd6982c92009-05-29 11:02:22 -04006878 if (nativeHasCursorNode() && !nativeCursorIsTextInput()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006879 playSoundEffect(SoundEffectConstants.CLICK);
6880 }
6881 }
6882
Cary Clarkb2601352011-01-11 11:32:01 -05006883 /**
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006884 * Returns plugin bounds if x/y in content coordinates corresponds to a
6885 * plugin. Otherwise a NULL rectangle is returned.
Cary Clarkb2601352011-01-11 11:32:01 -05006886 */
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006887 Rect getPluginBounds(int x, int y) {
Cary Clarke9290822011-02-22 13:20:56 -05006888 int slop = viewToContentDimension(mNavSlop);
6889 if (nativePointInNavCache(x, y, slop) && nativeCacheHitIsPlugin()) {
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006890 return nativeCacheHitNodeBounds();
6891 } else {
6892 return null;
6893 }
Cary Clarkb2601352011-01-11 11:32:01 -05006894 }
6895
Grace Klobac6f95fe2010-03-10 13:25:34 -08006896 /*
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006897 * Return true if the rect (e.g. plugin) is fully visible and maximized
6898 * inside the WebView.
Grace Klobac6f95fe2010-03-10 13:25:34 -08006899 */
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006900 boolean isRectFitOnScreen(Rect rect) {
6901 final int rectWidth = rect.width();
6902 final int rectHeight = rect.height();
Derek Sollenberger15c5ddb2010-06-10 12:31:29 -04006903 final int viewWidth = getViewWidth();
6904 final int viewHeight = getViewHeightWithTitle();
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006905 float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight / rectHeight);
Derek Sollenberger369aca22010-06-09 14:11:59 -04006906 scale = mZoomManager.computeScaleWithLimits(scale);
Derek Sollenberger15c5ddb2010-06-10 12:31:29 -04006907 return !mZoomManager.willScaleTriggerZoom(scale)
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006908 && contentToViewX(rect.left) >= mScrollX
6909 && contentToViewX(rect.right) <= mScrollX + viewWidth
6910 && contentToViewY(rect.top) >= mScrollY
6911 && contentToViewY(rect.bottom) <= mScrollY + viewHeight;
Grace Klobac6f95fe2010-03-10 13:25:34 -08006912 }
6913
6914 /*
Grace Klobae8300a12010-03-12 13:32:55 -08006915 * Maximize and center the rectangle, specified in the document coordinate
6916 * space, inside the WebView. If the zoom doesn't need to be changed, do an
6917 * animated scroll to center it. If the zoom needs to be changed, find the
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006918 * zoom center and do a smooth zoom transition. The rect is in document
6919 * coordinates
Grace Klobac6f95fe2010-03-10 13:25:34 -08006920 */
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006921 void centerFitRect(Rect rect) {
6922 final int rectWidth = rect.width();
6923 final int rectHeight = rect.height();
6924 final int viewWidth = getViewWidth();
6925 final int viewHeight = getViewHeightWithTitle();
6926 float scale = Math.min((float) viewWidth / rectWidth, (float) viewHeight
6927 / rectHeight);
Derek Sollenberger369aca22010-06-09 14:11:59 -04006928 scale = mZoomManager.computeScaleWithLimits(scale);
Derek Sollenberger03e48912010-05-18 17:03:42 -04006929 if (!mZoomManager.willScaleTriggerZoom(scale)) {
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006930 pinScrollTo(contentToViewX(rect.left + rectWidth / 2) - viewWidth / 2,
6931 contentToViewY(rect.top + rectHeight / 2) - viewHeight / 2,
Grace Klobac6f95fe2010-03-10 13:25:34 -08006932 true, 0);
6933 } else {
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04006934 float actualScale = mZoomManager.getScale();
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006935 float oldScreenX = rect.left * actualScale - mScrollX;
6936 float rectViewX = rect.left * scale;
6937 float rectViewWidth = rectWidth * scale;
Grace Kloba1e65d9e2010-03-12 19:19:48 -08006938 float newMaxWidth = mContentWidth * scale;
6939 float newScreenX = (viewWidth - rectViewWidth) / 2;
Grace Klobac6f95fe2010-03-10 13:25:34 -08006940 // pin the newX to the WebView
Grace Klobae8300a12010-03-12 13:32:55 -08006941 if (newScreenX > rectViewX) {
6942 newScreenX = rectViewX;
6943 } else if (newScreenX > (newMaxWidth - rectViewX - rectViewWidth)) {
6944 newScreenX = viewWidth - (newMaxWidth - rectViewX);
Grace Klobac6f95fe2010-03-10 13:25:34 -08006945 }
Derek Sollenberger03e48912010-05-18 17:03:42 -04006946 float zoomCenterX = (oldScreenX * scale - newScreenX * actualScale)
6947 / (scale - actualScale);
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006948 float oldScreenY = rect.top * actualScale + getTitleHeight()
Grace Kloba1e65d9e2010-03-12 19:19:48 -08006949 - mScrollY;
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05006950 float rectViewY = rect.top * scale + getTitleHeight();
6951 float rectViewHeight = rectHeight * scale;
Grace Kloba1e65d9e2010-03-12 19:19:48 -08006952 float newMaxHeight = mContentHeight * scale + getTitleHeight();
6953 float newScreenY = (viewHeight - rectViewHeight) / 2;
Grace Klobac6f95fe2010-03-10 13:25:34 -08006954 // pin the newY to the WebView
Grace Klobae8300a12010-03-12 13:32:55 -08006955 if (newScreenY > rectViewY) {
6956 newScreenY = rectViewY;
6957 } else if (newScreenY > (newMaxHeight - rectViewY - rectViewHeight)) {
6958 newScreenY = viewHeight - (newMaxHeight - rectViewY);
Grace Klobac6f95fe2010-03-10 13:25:34 -08006959 }
Derek Sollenberger03e48912010-05-18 17:03:42 -04006960 float zoomCenterY = (oldScreenY * scale - newScreenY * actualScale)
6961 / (scale - actualScale);
6962 mZoomManager.setZoomCenter(zoomCenterX, zoomCenterY);
Derek Sollenberger87b17be52010-06-01 11:49:31 -04006963 mZoomManager.startZoomAnimation(scale, false);
Grace Klobac6f95fe2010-03-10 13:25:34 -08006964 }
6965 }
6966
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006967 // Called by JNI to handle a touch on a node representing an email address,
6968 // address, or phone number
6969 private void overrideLoading(String url) {
6970 mCallbackProxy.uiOverrideUrlLoading(url);
6971 }
6972
6973 @Override
6974 public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
Leon Scroggins III26723fc2010-04-19 13:21:42 -04006975 // FIXME: If a subwindow is showing find, and the user touches the
6976 // background window, it can steal focus.
6977 if (mFindIsUp) return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006978 boolean result = false;
6979 if (inEditingMode()) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04006980 result = mWebTextView.requestFocus(direction,
6981 previouslyFocusedRect);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006982 } else {
6983 result = super.requestFocus(direction, previouslyFocusedRect);
Leon Scroggins572ba782011-01-28 11:25:53 -05006984 if (mWebViewCore.getSettings().getNeedInitialFocus() && !isInTouchMode()) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08006985 // For cases such as GMail, where we gain focus from a direction,
6986 // we want to move to the first available link.
6987 // FIXME: If there are no visible links, we may not want to
6988 int fakeKeyDirection = 0;
6989 switch(direction) {
6990 case View.FOCUS_UP:
6991 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_UP;
6992 break;
6993 case View.FOCUS_DOWN:
6994 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_DOWN;
6995 break;
6996 case View.FOCUS_LEFT:
6997 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_LEFT;
6998 break;
6999 case View.FOCUS_RIGHT:
7000 fakeKeyDirection = KeyEvent.KEYCODE_DPAD_RIGHT;
7001 break;
7002 default:
7003 return result;
7004 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007005 if (mNativeClass != 0 && !nativeHasCursorNode()) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05007006 navHandledKey(fakeKeyDirection, 1, true, 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007007 }
7008 }
7009 }
7010 return result;
7011 }
7012
7013 @Override
7014 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
7015 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
7016
7017 int heightMode = MeasureSpec.getMode(heightMeasureSpec);
7018 int heightSize = MeasureSpec.getSize(heightMeasureSpec);
7019 int widthMode = MeasureSpec.getMode(widthMeasureSpec);
7020 int widthSize = MeasureSpec.getSize(widthMeasureSpec);
7021
7022 int measuredHeight = heightSize;
7023 int measuredWidth = widthSize;
7024
7025 // Grab the content size from WebViewCore.
Grace Klobae621d6f2009-09-11 13:20:39 -07007026 int contentHeight = contentToViewDimension(mContentHeight);
7027 int contentWidth = contentToViewDimension(mContentWidth);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007028
7029// Log.d(LOGTAG, "------- measure " + heightMode);
7030
7031 if (heightMode != MeasureSpec.EXACTLY) {
7032 mHeightCanMeasure = true;
7033 measuredHeight = contentHeight;
7034 if (heightMode == MeasureSpec.AT_MOST) {
7035 // If we are larger than the AT_MOST height, then our height can
7036 // no longer be measured and we should scroll internally.
7037 if (measuredHeight > heightSize) {
7038 measuredHeight = heightSize;
7039 mHeightCanMeasure = false;
Dianne Hackborn189ee182010-12-02 21:48:53 -08007040 } else if (measuredHeight < heightSize) {
7041 measuredHeight |= MEASURED_STATE_TOO_SMALL;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007042 }
7043 }
7044 } else {
7045 mHeightCanMeasure = false;
7046 }
7047 if (mNativeClass != 0) {
7048 nativeSetHeightCanMeasure(mHeightCanMeasure);
7049 }
7050 // For the width, always use the given size unless unspecified.
7051 if (widthMode == MeasureSpec.UNSPECIFIED) {
7052 mWidthCanMeasure = true;
7053 measuredWidth = contentWidth;
7054 } else {
Dianne Hackborn189ee182010-12-02 21:48:53 -08007055 if (measuredWidth < contentWidth) {
7056 measuredWidth |= MEASURED_STATE_TOO_SMALL;
7057 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007058 mWidthCanMeasure = false;
7059 }
7060
7061 synchronized (this) {
7062 setMeasuredDimension(measuredWidth, measuredHeight);
7063 }
7064 }
7065
7066 @Override
7067 public boolean requestChildRectangleOnScreen(View child,
7068 Rect rect,
7069 boolean immediate) {
Cary Clarkdc09b5a2010-12-07 08:48:10 -05007070 if (mNativeClass == 0) {
7071 return false;
7072 }
Derek Sollenberger03e48912010-05-18 17:03:42 -04007073 // don't scroll while in zoom animation. When it is done, we will adjust
7074 // the necessary components (e.g., WebTextView if it is in editing mode)
Derek Sollenberger293c3602010-06-04 10:44:48 -04007075 if (mZoomManager.isFixedLengthAnimationInProgress()) {
Derek Sollenberger03e48912010-05-18 17:03:42 -04007076 return false;
7077 }
7078
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007079 rect.offset(child.getLeft() - child.getScrollX(),
7080 child.getTop() - child.getScrollY());
7081
Cary Clark31b83672010-03-09 09:20:34 -05007082 Rect content = new Rect(viewToContentX(mScrollX),
7083 viewToContentY(mScrollY),
7084 viewToContentX(mScrollX + getWidth()
7085 - getVerticalScrollbarWidth()),
7086 viewToContentY(mScrollY + getViewHeightWithTitle()));
7087 content = nativeSubtractLayers(content);
7088 int screenTop = contentToViewY(content.top);
7089 int screenBottom = contentToViewY(content.bottom);
7090 int height = screenBottom - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007091 int scrollYDelta = 0;
7092
Leon Scrogginsbed911a2009-03-31 14:29:35 -07007093 if (rect.bottom > screenBottom) {
7094 int oneThirdOfScreenHeight = height / 3;
7095 if (rect.height() > 2 * oneThirdOfScreenHeight) {
7096 // If the rectangle is too tall to fit in the bottom two thirds
7097 // of the screen, place it at the top.
7098 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007099 } else {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07007100 // If the rectangle will still fit on screen, we want its
7101 // top to be in the top third of the screen.
7102 scrollYDelta = rect.top - (screenTop + oneThirdOfScreenHeight);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007103 }
7104 } else if (rect.top < screenTop) {
Leon Scrogginsbed911a2009-03-31 14:29:35 -07007105 scrollYDelta = rect.top - screenTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007106 }
7107
Cary Clark31b83672010-03-09 09:20:34 -05007108 int screenLeft = contentToViewX(content.left);
7109 int screenRight = contentToViewX(content.right);
7110 int width = screenRight - screenLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007111 int scrollXDelta = 0;
7112
7113 if (rect.right > screenRight && rect.left > screenLeft) {
7114 if (rect.width() > width) {
7115 scrollXDelta += (rect.left - screenLeft);
7116 } else {
7117 scrollXDelta += (rect.right - screenRight);
7118 }
7119 } else if (rect.left < screenLeft) {
7120 scrollXDelta -= (screenLeft - rect.left);
7121 }
7122
7123 if ((scrollYDelta | scrollXDelta) != 0) {
7124 return pinScrollBy(scrollXDelta, scrollYDelta, !immediate, 0);
7125 }
7126
7127 return false;
7128 }
Cary Clarkd6982c92009-05-29 11:02:22 -04007129
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007130 /* package */ void replaceTextfieldText(int oldStart, int oldEnd,
7131 String replace, int newStart, int newEnd) {
Cary Clarkded054c2009-06-15 10:26:08 -04007132 WebViewCore.ReplaceTextData arg = new WebViewCore.ReplaceTextData();
7133 arg.mReplace = replace;
7134 arg.mNewStart = newStart;
7135 arg.mNewEnd = newEnd;
The Android Open Source Projectb2a3dd82009-03-09 11:52:12 -07007136 mTextGeneration++;
Leon Scroggins43488fc2009-07-06 14:32:49 -04007137 arg.mTextGeneration = mTextGeneration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007138 mWebViewCore.sendMessage(EventHub.REPLACE_TEXT, oldStart, oldEnd, arg);
7139 }
7140
7141 /* package */ void passToJavaScript(String currentText, KeyEvent event) {
Cary Clarkded054c2009-06-15 10:26:08 -04007142 WebViewCore.JSKeyData arg = new WebViewCore.JSKeyData();
7143 arg.mEvent = event;
7144 arg.mCurrentText = currentText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007145 // Increase our text generation number, and pass it to webcore thread
7146 mTextGeneration++;
7147 mWebViewCore.sendMessage(EventHub.PASS_TO_JS, mTextGeneration, 0, arg);
7148 // WebKit's document state is not saved until about to leave the page.
Cary Clarkd6982c92009-05-29 11:02:22 -04007149 // To make sure the host application, like Browser, has the up to date
7150 // document state when it goes to background, we force to save the
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007151 // document state.
7152 mWebViewCore.removeMessages(EventHub.SAVE_DOCUMENT_STATE);
7153 mWebViewCore.sendMessageDelayed(EventHub.SAVE_DOCUMENT_STATE,
Cary Clarkd6982c92009-05-29 11:02:22 -04007154 cursorData(), 1000);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007155 }
7156
Andrei Popescu04098572010-03-09 12:23:19 +00007157 /* package */ synchronized WebViewCore getWebViewCore() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007158 return mWebViewCore;
7159 }
7160
Adam Powell4fb35d42011-03-03 17:54:55 -08007161 /**
7162 * Used only by TouchEventQueue to store pending touch events.
7163 */
7164 private static class QueuedTouch {
7165 long mSequence;
7166 MotionEvent mEvent; // Optional
7167 TouchEventData mTed; // Optional
7168
7169 QueuedTouch mNext;
7170
7171 public QueuedTouch set(TouchEventData ted) {
7172 mSequence = ted.mSequence;
7173 mTed = ted;
7174 mEvent = null;
7175 mNext = null;
7176 return this;
7177 }
7178
7179 public QueuedTouch set(MotionEvent ev, long sequence) {
7180 mEvent = MotionEvent.obtain(ev);
7181 mSequence = sequence;
7182 mTed = null;
7183 mNext = null;
7184 return this;
7185 }
7186
7187 public QueuedTouch add(QueuedTouch other) {
7188 if (other.mSequence < mSequence) {
7189 other.mNext = this;
7190 return other;
7191 }
7192
7193 QueuedTouch insertAt = this;
7194 while (insertAt.mNext != null && insertAt.mNext.mSequence < other.mSequence) {
7195 insertAt = insertAt.mNext;
7196 }
7197 other.mNext = insertAt.mNext;
7198 insertAt.mNext = other;
7199 return this;
7200 }
7201 }
7202
7203 /**
7204 * WebView handles touch events asynchronously since some events must be passed to WebKit
7205 * for potentially slower processing. TouchEventQueue serializes touch events regardless
7206 * of which path they take to ensure that no events are ever processed out of order
7207 * by WebView.
7208 */
7209 private class TouchEventQueue {
7210 private long mNextTouchSequence = Long.MIN_VALUE + 1;
7211 private long mLastHandledTouchSequence = Long.MIN_VALUE;
Adam Powelld0197f32011-03-17 00:09:03 -07007212 private long mIgnoreUntilSequence = Long.MIN_VALUE + 1;
Adam Powell3c534772011-04-04 14:27:12 -07007213
7214 // Events waiting to be processed.
Adam Powell4fb35d42011-03-03 17:54:55 -08007215 private QueuedTouch mTouchEventQueue;
Adam Powell3c534772011-04-04 14:27:12 -07007216
7217 // Known events that are waiting on a response before being enqueued.
7218 private QueuedTouch mPreQueue;
7219
7220 // Pool of QueuedTouch objects saved for later use.
Adam Powell4fb35d42011-03-03 17:54:55 -08007221 private QueuedTouch mQueuedTouchRecycleBin;
7222 private int mQueuedTouchRecycleCount;
Adam Powell3c534772011-04-04 14:27:12 -07007223
Adam Powelld0197f32011-03-17 00:09:03 -07007224 private long mLastEventTime = Long.MAX_VALUE;
Adam Powell4fb35d42011-03-03 17:54:55 -08007225 private static final int MAX_RECYCLED_QUEUED_TOUCH = 15;
7226
Adam Powell80615b02011-03-10 15:53:24 -08007227 // milliseconds until we abandon hope of getting all of a previous gesture
Adam Powelld0197f32011-03-17 00:09:03 -07007228 private static final int QUEUED_GESTURE_TIMEOUT = 1000;
Adam Powell80615b02011-03-10 15:53:24 -08007229
Adam Powell4fb35d42011-03-03 17:54:55 -08007230 private QueuedTouch obtainQueuedTouch() {
7231 if (mQueuedTouchRecycleBin != null) {
7232 QueuedTouch result = mQueuedTouchRecycleBin;
7233 mQueuedTouchRecycleBin = result.mNext;
7234 mQueuedTouchRecycleCount--;
7235 return result;
7236 }
7237 return new QueuedTouch();
7238 }
7239
Adam Powell80615b02011-03-10 15:53:24 -08007240 /**
7241 * Allow events with any currently missing sequence numbers to be skipped in processing.
7242 */
7243 public void ignoreCurrentlyMissingEvents() {
7244 mIgnoreUntilSequence = mNextTouchSequence;
Adam Powell3c534772011-04-04 14:27:12 -07007245
7246 // Run any events we have available and complete, pre-queued or otherwise.
7247 runQueuedAndPreQueuedEvents();
7248 }
7249
7250 private void runQueuedAndPreQueuedEvents() {
7251 QueuedTouch qd = mPreQueue;
7252 boolean fromPreQueue = true;
7253 while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
7254 handleQueuedTouch(qd);
7255 QueuedTouch recycleMe = qd;
7256 if (fromPreQueue) {
7257 mPreQueue = qd.mNext;
7258 } else {
7259 mTouchEventQueue = qd.mNext;
7260 }
7261 recycleQueuedTouch(recycleMe);
7262 mLastHandledTouchSequence++;
7263
7264 long nextPre = mPreQueue != null ? mPreQueue.mSequence : Long.MAX_VALUE;
7265 long nextQueued = mTouchEventQueue != null ?
7266 mTouchEventQueue.mSequence : Long.MAX_VALUE;
7267 fromPreQueue = nextPre < nextQueued;
7268 qd = fromPreQueue ? mPreQueue : mTouchEventQueue;
7269 }
7270 }
7271
7272 /**
7273 * Add a TouchEventData to the pre-queue.
7274 *
7275 * An event in the pre-queue is an event that we know about that
7276 * has been sent to webkit, but that we haven't received back and
7277 * enqueued into the normal touch queue yet. If webkit ever times
7278 * out and we need to ignore currently missing events, we'll run
7279 * events from the pre-queue to patch the holes.
7280 *
7281 * @param ted TouchEventData to pre-queue
7282 */
7283 public void preQueueTouchEventData(TouchEventData ted) {
7284 QueuedTouch newTouch = obtainQueuedTouch().set(ted);
7285 if (mPreQueue == null) {
7286 mPreQueue = newTouch;
7287 } else {
7288 QueuedTouch insertionPoint = mPreQueue;
7289 while (insertionPoint.mNext != null &&
7290 insertionPoint.mNext.mSequence < newTouch.mSequence) {
7291 insertionPoint = insertionPoint.mNext;
7292 }
7293 newTouch.mNext = insertionPoint.mNext;
7294 insertionPoint.mNext = newTouch;
7295 }
Adam Powell80615b02011-03-10 15:53:24 -08007296 }
7297
Adam Powell4fb35d42011-03-03 17:54:55 -08007298 private void recycleQueuedTouch(QueuedTouch qd) {
7299 if (mQueuedTouchRecycleCount < MAX_RECYCLED_QUEUED_TOUCH) {
7300 qd.mNext = mQueuedTouchRecycleBin;
7301 mQueuedTouchRecycleBin = qd;
7302 mQueuedTouchRecycleCount++;
7303 }
7304 }
7305
7306 /**
7307 * Reset the touch event queue. This will dump any pending events
7308 * and reset the sequence numbering.
7309 */
7310 public void reset() {
7311 mNextTouchSequence = Long.MIN_VALUE + 1;
7312 mLastHandledTouchSequence = Long.MIN_VALUE;
Adam Powelld0197f32011-03-17 00:09:03 -07007313 mIgnoreUntilSequence = Long.MIN_VALUE + 1;
Adam Powell4fb35d42011-03-03 17:54:55 -08007314 while (mTouchEventQueue != null) {
7315 QueuedTouch recycleMe = mTouchEventQueue;
7316 mTouchEventQueue = mTouchEventQueue.mNext;
7317 recycleQueuedTouch(recycleMe);
7318 }
Adam Powell3c534772011-04-04 14:27:12 -07007319 while (mPreQueue != null) {
7320 QueuedTouch recycleMe = mPreQueue;
7321 mPreQueue = mPreQueue.mNext;
7322 recycleQueuedTouch(recycleMe);
7323 }
Adam Powell4fb35d42011-03-03 17:54:55 -08007324 }
7325
7326 /**
7327 * Return the next valid sequence number for tagging incoming touch events.
7328 * @return The next touch event sequence number
7329 */
7330 public long nextTouchSequence() {
7331 return mNextTouchSequence++;
7332 }
7333
7334 /**
7335 * Enqueue a touch event in the form of TouchEventData.
7336 * The sequence number will be read from the mSequence field of the argument.
7337 *
7338 * If the touch event's sequence number is the next in line to be processed, it will
7339 * be handled before this method returns. Any subsequent events that have already
7340 * been queued will also be processed in their proper order.
7341 *
7342 * @param ted Touch data to be processed in order.
Adam Powellbaa33802011-03-25 13:58:19 -07007343 * @return true if the event was processed before returning, false if it was just enqueued.
Adam Powell4fb35d42011-03-03 17:54:55 -08007344 */
Adam Powellbaa33802011-03-25 13:58:19 -07007345 public boolean enqueueTouchEvent(TouchEventData ted) {
Adam Powell3c534772011-04-04 14:27:12 -07007346 // Remove from the pre-queue if present
7347 QueuedTouch preQueue = mPreQueue;
7348 if (preQueue != null) {
7349 // On exiting this block, preQueue is set to the pre-queued QueuedTouch object
7350 // if it was present in the pre-queue, and removed from the pre-queue itself.
7351 if (preQueue.mSequence == ted.mSequence) {
7352 mPreQueue = preQueue.mNext;
7353 } else {
7354 QueuedTouch prev = preQueue;
7355 preQueue = null;
7356 while (prev.mNext != null) {
7357 if (prev.mNext.mSequence == ted.mSequence) {
7358 preQueue = prev.mNext;
7359 prev.mNext = preQueue.mNext;
7360 break;
7361 } else {
7362 prev = prev.mNext;
7363 }
7364 }
7365 }
7366 }
7367
Adam Powell80615b02011-03-10 15:53:24 -08007368 if (ted.mSequence < mLastHandledTouchSequence) {
7369 // Stale event and we already moved on; drop it. (Should not be common.)
7370 Log.w(LOGTAG, "Stale touch event " + MotionEvent.actionToString(ted.mAction) +
7371 " received from webcore; ignoring");
Adam Powellbaa33802011-03-25 13:58:19 -07007372 return false;
Adam Powell80615b02011-03-10 15:53:24 -08007373 }
7374
Adam Powelld0197f32011-03-17 00:09:03 -07007375 if (dropStaleGestures(ted.mMotionEvent, ted.mSequence)) {
Adam Powellbaa33802011-03-25 13:58:19 -07007376 return false;
Adam Powelld0197f32011-03-17 00:09:03 -07007377 }
Adam Powell80615b02011-03-10 15:53:24 -08007378
Adam Powell3c534772011-04-04 14:27:12 -07007379 // dropStaleGestures above might have fast-forwarded us to
7380 // an event we have already.
7381 runNextQueuedEvents();
7382
Adam Powell4fb35d42011-03-03 17:54:55 -08007383 if (mLastHandledTouchSequence + 1 == ted.mSequence) {
Adam Powell3c534772011-04-04 14:27:12 -07007384 if (preQueue != null) {
7385 recycleQueuedTouch(preQueue);
7386 preQueue = null;
7387 }
Adam Powell4fb35d42011-03-03 17:54:55 -08007388 handleQueuedTouchEventData(ted);
7389
7390 mLastHandledTouchSequence++;
7391
7392 // Do we have any more? Run them if so.
Adam Powell3c534772011-04-04 14:27:12 -07007393 runNextQueuedEvents();
Adam Powell4fb35d42011-03-03 17:54:55 -08007394 } else {
Adam Powell3c534772011-04-04 14:27:12 -07007395 // Reuse the pre-queued object if we had it.
7396 QueuedTouch qd = preQueue != null ? preQueue : obtainQueuedTouch().set(ted);
Adam Powell4fb35d42011-03-03 17:54:55 -08007397 mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
7398 }
Adam Powellbaa33802011-03-25 13:58:19 -07007399 return true;
Adam Powell4fb35d42011-03-03 17:54:55 -08007400 }
7401
7402 /**
7403 * Enqueue a touch event in the form of a MotionEvent from the framework.
7404 *
7405 * If the touch event's sequence number is the next in line to be processed, it will
7406 * be handled before this method returns. Any subsequent events that have already
7407 * been queued will also be processed in their proper order.
7408 *
7409 * @param ev MotionEvent to be processed in order
7410 */
7411 public void enqueueTouchEvent(MotionEvent ev) {
7412 final long sequence = nextTouchSequence();
Adam Powell80615b02011-03-10 15:53:24 -08007413
Adam Powelld0197f32011-03-17 00:09:03 -07007414 if (dropStaleGestures(ev, sequence)) {
7415 return;
7416 }
Adam Powell80615b02011-03-10 15:53:24 -08007417
Adam Powell3c534772011-04-04 14:27:12 -07007418 // dropStaleGestures above might have fast-forwarded us to
7419 // an event we have already.
7420 runNextQueuedEvents();
7421
Adam Powell4fb35d42011-03-03 17:54:55 -08007422 if (mLastHandledTouchSequence + 1 == sequence) {
7423 handleQueuedMotionEvent(ev);
7424
7425 mLastHandledTouchSequence++;
7426
7427 // Do we have any more? Run them if so.
Adam Powell3c534772011-04-04 14:27:12 -07007428 runNextQueuedEvents();
Adam Powell4fb35d42011-03-03 17:54:55 -08007429 } else {
7430 QueuedTouch qd = obtainQueuedTouch().set(ev, sequence);
7431 mTouchEventQueue = mTouchEventQueue == null ? qd : mTouchEventQueue.add(qd);
7432 }
7433 }
7434
Adam Powell3c534772011-04-04 14:27:12 -07007435 private void runNextQueuedEvents() {
7436 QueuedTouch qd = mTouchEventQueue;
7437 while (qd != null && qd.mSequence == mLastHandledTouchSequence + 1) {
7438 handleQueuedTouch(qd);
7439 QueuedTouch recycleMe = qd;
7440 qd = qd.mNext;
7441 recycleQueuedTouch(recycleMe);
7442 mLastHandledTouchSequence++;
7443 }
7444 mTouchEventQueue = qd;
7445 }
7446
Adam Powelld0197f32011-03-17 00:09:03 -07007447 private boolean dropStaleGestures(MotionEvent ev, long sequence) {
7448 if (ev != null && ev.getAction() == MotionEvent.ACTION_MOVE && !mConfirmMove) {
7449 // This is to make sure that we don't attempt to process a tap
7450 // or long press when webkit takes too long to get back to us.
7451 // The movement will be properly confirmed when we process the
7452 // enqueued event later.
7453 final int dx = Math.round(ev.getX()) - mLastTouchX;
7454 final int dy = Math.round(ev.getY()) - mLastTouchY;
7455 if (dx * dx + dy * dy > mTouchSlopSquare) {
7456 mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
7457 mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
7458 }
7459 }
Adam Powell8f626a12011-03-10 19:27:15 -08007460
Adam Powelld0197f32011-03-17 00:09:03 -07007461 if (mTouchEventQueue == null) {
7462 return sequence <= mLastHandledTouchSequence;
7463 }
Adam Powell8f626a12011-03-10 19:27:15 -08007464
Adam Powelld0197f32011-03-17 00:09:03 -07007465 // If we have a new down event and it's been a while since the last event
Adam Powell3c534772011-04-04 14:27:12 -07007466 // we saw, catch up as best we can and keep going.
Adam Powelld0197f32011-03-17 00:09:03 -07007467 if (ev != null && ev.getAction() == MotionEvent.ACTION_DOWN) {
Adam Powell80615b02011-03-10 15:53:24 -08007468 long eventTime = ev.getEventTime();
Adam Powelld0197f32011-03-17 00:09:03 -07007469 long lastHandledEventTime = mLastEventTime;
7470 if (eventTime > lastHandledEventTime + QUEUED_GESTURE_TIMEOUT) {
Adam Powell80615b02011-03-10 15:53:24 -08007471 Log.w(LOGTAG, "Got ACTION_DOWN but still waiting on stale event. " +
Adam Powell3c534772011-04-04 14:27:12 -07007472 "Catching up.");
7473 runQueuedAndPreQueuedEvents();
7474
7475 // Drop leftovers that we truly don't have.
Adam Powell80615b02011-03-10 15:53:24 -08007476 QueuedTouch qd = mTouchEventQueue;
7477 while (qd != null && qd.mSequence < sequence) {
7478 QueuedTouch recycleMe = qd;
7479 qd = qd.mNext;
7480 recycleQueuedTouch(recycleMe);
7481 }
7482 mTouchEventQueue = qd;
7483 mLastHandledTouchSequence = sequence - 1;
7484 }
7485 }
7486
Adam Powelld0197f32011-03-17 00:09:03 -07007487 if (mIgnoreUntilSequence - 1 > mLastHandledTouchSequence) {
Adam Powell80615b02011-03-10 15:53:24 -08007488 QueuedTouch qd = mTouchEventQueue;
Adam Powelld0197f32011-03-17 00:09:03 -07007489 while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
Adam Powell80615b02011-03-10 15:53:24 -08007490 QueuedTouch recycleMe = qd;
7491 qd = qd.mNext;
7492 recycleQueuedTouch(recycleMe);
7493 }
7494 mTouchEventQueue = qd;
Adam Powelld0197f32011-03-17 00:09:03 -07007495 mLastHandledTouchSequence = mIgnoreUntilSequence - 1;
Adam Powell80615b02011-03-10 15:53:24 -08007496 }
Adam Powelld0197f32011-03-17 00:09:03 -07007497
Adam Powell3c534772011-04-04 14:27:12 -07007498 if (mPreQueue != null) {
7499 // Drop stale prequeued events
7500 QueuedTouch qd = mPreQueue;
7501 while (qd != null && qd.mSequence < mIgnoreUntilSequence) {
7502 QueuedTouch recycleMe = qd;
7503 qd = qd.mNext;
7504 recycleQueuedTouch(recycleMe);
7505 }
7506 mPreQueue = qd;
7507 }
7508
Adam Powelld0197f32011-03-17 00:09:03 -07007509 return sequence <= mLastHandledTouchSequence;
Adam Powell80615b02011-03-10 15:53:24 -08007510 }
7511
Adam Powell4fb35d42011-03-03 17:54:55 -08007512 private void handleQueuedTouch(QueuedTouch qt) {
7513 if (qt.mTed != null) {
7514 handleQueuedTouchEventData(qt.mTed);
7515 } else {
7516 handleQueuedMotionEvent(qt.mEvent);
7517 qt.mEvent.recycle();
7518 }
7519 }
7520
7521 private void handleQueuedMotionEvent(MotionEvent ev) {
Adam Powelld0197f32011-03-17 00:09:03 -07007522 mLastEventTime = ev.getEventTime();
Adam Powell4fb35d42011-03-03 17:54:55 -08007523 int action = ev.getActionMasked();
7524 if (ev.getPointerCount() > 1) { // Multi-touch
Adam Powell4fb35d42011-03-03 17:54:55 -08007525 handleMultiTouchInWebView(ev);
7526 } else {
7527 final ScaleGestureDetector detector = mZoomManager.getMultiTouchGestureDetector();
Adam Powell8f626a12011-03-10 19:27:15 -08007528 if (detector != null && mPreventDefault != PREVENT_DEFAULT_YES) {
Adam Powell4fb35d42011-03-03 17:54:55 -08007529 // ScaleGestureDetector needs a consistent event stream to operate properly.
7530 // It won't take any action with fewer than two pointers, but it needs to
7531 // update internal bookkeeping state.
7532 detector.onTouchEvent(ev);
7533 }
7534
7535 handleTouchEventCommon(ev, action, Math.round(ev.getX()), Math.round(ev.getY()));
7536 }
7537 }
7538
7539 private void handleQueuedTouchEventData(TouchEventData ted) {
Adam Powelld0197f32011-03-17 00:09:03 -07007540 if (ted.mMotionEvent != null) {
7541 mLastEventTime = ted.mMotionEvent.getEventTime();
7542 }
Adam Powell4fb35d42011-03-03 17:54:55 -08007543 if (!ted.mReprocess) {
7544 if (ted.mAction == MotionEvent.ACTION_DOWN
7545 && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES) {
7546 // if prevent default is called from WebCore, UI
7547 // will not handle the rest of the touch events any
7548 // more.
7549 mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
7550 : PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN;
7551 } else if (ted.mAction == MotionEvent.ACTION_MOVE
7552 && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN) {
7553 // the return for the first ACTION_MOVE will decide
7554 // whether UI will handle touch or not. Currently no
7555 // support for alternating prevent default
7556 mPreventDefault = ted.mNativeResult ? PREVENT_DEFAULT_YES
7557 : PREVENT_DEFAULT_NO;
7558 }
7559 if (mPreventDefault == PREVENT_DEFAULT_YES) {
7560 mTouchHighlightRegion.setEmpty();
7561 }
7562 } else {
7563 if (ted.mPoints.length > 1) { // multi-touch
Adam Powell8f626a12011-03-10 19:27:15 -08007564 if (!ted.mNativeResult && mPreventDefault != PREVENT_DEFAULT_YES) {
Adam Powell4fb35d42011-03-03 17:54:55 -08007565 mPreventDefault = PREVENT_DEFAULT_NO;
7566 handleMultiTouchInWebView(ted.mMotionEvent);
7567 } else {
7568 mPreventDefault = PREVENT_DEFAULT_YES;
7569 }
7570 return;
7571 }
7572
7573 // prevent default is not called in WebCore, so the
7574 // message needs to be reprocessed in UI
7575 if (!ted.mNativeResult) {
7576 // Following is for single touch.
7577 switch (ted.mAction) {
7578 case MotionEvent.ACTION_DOWN:
Huahui Wu88b869a2011-03-17 17:42:12 -07007579 mLastDeferTouchX = ted.mPointsInView[0].x;
7580 mLastDeferTouchY = ted.mPointsInView[0].y;
Adam Powell4fb35d42011-03-03 17:54:55 -08007581 mDeferTouchMode = TOUCH_INIT_MODE;
7582 break;
7583 case MotionEvent.ACTION_MOVE: {
7584 // no snapping in defer process
Huahui Wu88b869a2011-03-17 17:42:12 -07007585 int x = ted.mPointsInView[0].x;
7586 int y = ted.mPointsInView[0].y;
7587
Adam Powell4fb35d42011-03-03 17:54:55 -08007588 if (mDeferTouchMode != TOUCH_DRAG_MODE) {
7589 mDeferTouchMode = TOUCH_DRAG_MODE;
7590 mLastDeferTouchX = x;
7591 mLastDeferTouchY = y;
7592 startScrollingLayer(x, y);
7593 startDrag();
7594 }
7595 int deltaX = pinLocX((int) (mScrollX
7596 + mLastDeferTouchX - x))
7597 - mScrollX;
7598 int deltaY = pinLocY((int) (mScrollY
7599 + mLastDeferTouchY - y))
7600 - mScrollY;
7601 doDrag(deltaX, deltaY);
7602 if (deltaX != 0) mLastDeferTouchX = x;
7603 if (deltaY != 0) mLastDeferTouchY = y;
7604 break;
7605 }
7606 case MotionEvent.ACTION_UP:
7607 case MotionEvent.ACTION_CANCEL:
7608 if (mDeferTouchMode == TOUCH_DRAG_MODE) {
7609 // no fling in defer process
7610 mScroller.springBack(mScrollX, mScrollY, 0,
7611 computeMaxScrollX(), 0,
7612 computeMaxScrollY());
7613 invalidate();
7614 WebViewCore.resumePriority();
7615 WebViewCore.resumeUpdatePicture(mWebViewCore);
7616 }
7617 mDeferTouchMode = TOUCH_DONE_MODE;
7618 break;
7619 case WebViewCore.ACTION_DOUBLETAP:
7620 // doDoubleTap() needs mLastTouchX/Y as anchor
Huahui Wu88b869a2011-03-17 17:42:12 -07007621 mLastDeferTouchX = ted.mPointsInView[0].x;
7622 mLastDeferTouchY = ted.mPointsInView[0].y;
Adam Powell4fb35d42011-03-03 17:54:55 -08007623 mZoomManager.handleDoubleTap(mLastTouchX, mLastTouchY);
7624 mDeferTouchMode = TOUCH_DONE_MODE;
7625 break;
7626 case WebViewCore.ACTION_LONGPRESS:
7627 HitTestResult hitTest = getHitTestResult();
7628 if (hitTest != null && hitTest.mType
7629 != HitTestResult.UNKNOWN_TYPE) {
7630 performLongClick();
7631 }
7632 mDeferTouchMode = TOUCH_DONE_MODE;
7633 break;
7634 }
7635 }
7636 }
7637 }
7638 }
7639
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007640 //-------------------------------------------------------------------------
7641 // Methods can be called from a separate thread, like WebViewCore
7642 // If it needs to call the View system, it has to send message.
7643 //-------------------------------------------------------------------------
7644
7645 /**
7646 * General handler to receive message coming from webkit thread
7647 */
7648 class PrivateHandler extends Handler {
7649 @Override
7650 public void handleMessage(Message msg) {
Cary Clark3e88ddc2009-10-08 14:59:46 -04007651 // exclude INVAL_RECT_MSG_ID since it is frequently output
7652 if (DebugFlags.WEB_VIEW && msg.what != INVAL_RECT_MSG_ID) {
Grace Klobac2242f22010-03-05 14:00:26 -08007653 if (msg.what >= FIRST_PRIVATE_MSG_ID
7654 && msg.what <= LAST_PRIVATE_MSG_ID) {
7655 Log.v(LOGTAG, HandlerPrivateDebugString[msg.what
7656 - FIRST_PRIVATE_MSG_ID]);
7657 } else if (msg.what >= FIRST_PACKAGE_MSG_ID
7658 && msg.what <= LAST_PACKAGE_MSG_ID) {
7659 Log.v(LOGTAG, HandlerPackageDebugString[msg.what
7660 - FIRST_PACKAGE_MSG_ID]);
7661 } else {
7662 Log.v(LOGTAG, Integer.toString(msg.what));
7663 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007664 }
Grace Kloba207308a2009-09-27 11:44:14 -07007665 if (mWebViewCore == null) {
7666 // after WebView's destroy() is called, skip handling messages.
7667 return;
7668 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007669 switch (msg.what) {
7670 case REMEMBER_PASSWORD: {
7671 mDatabase.setUsernamePassword(
7672 msg.getData().getString("host"),
7673 msg.getData().getString("username"),
7674 msg.getData().getString("password"));
7675 ((Message) msg.obj).sendToTarget();
7676 break;
7677 }
7678 case NEVER_REMEMBER_PASSWORD: {
7679 mDatabase.setUsernamePassword(
7680 msg.getData().getString("host"), null, null);
7681 ((Message) msg.obj).sendToTarget();
7682 break;
7683 }
Grace Klobac2242f22010-03-05 14:00:26 -08007684 case PREVENT_DEFAULT_TIMEOUT: {
7685 // if timeout happens, cancel it so that it won't block UI
7686 // to continue handling touch events
7687 if ((msg.arg1 == MotionEvent.ACTION_DOWN
7688 && mPreventDefault == PREVENT_DEFAULT_MAYBE_YES)
7689 || (msg.arg1 == MotionEvent.ACTION_MOVE
7690 && mPreventDefault == PREVENT_DEFAULT_NO_FROM_TOUCH_DOWN)) {
7691 cancelWebCoreTouchEvent(
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08007692 viewToContentX(mLastTouchX + mScrollX),
7693 viewToContentY(mLastTouchY + mScrollY),
Grace Klobac2242f22010-03-05 14:00:26 -08007694 true);
Grace Klobaf58af622009-09-24 17:41:23 -07007695 }
Grace Klobac2242f22010-03-05 14:00:26 -08007696 break;
7697 }
Cary Clark6f5dfc62010-11-11 13:09:20 -05007698 case SCROLL_SELECT_TEXT: {
7699 if (mAutoScrollX == 0 && mAutoScrollY == 0) {
7700 mSentAutoScrollMessage = false;
7701 break;
7702 }
Cary Clarkb9aaa772011-01-07 16:14:54 -05007703 if (mScrollingLayer == 0) {
7704 pinScrollBy(mAutoScrollX, mAutoScrollY, true, 0);
7705 } else {
7706 mScrollingLayerRect.left += mAutoScrollX;
7707 mScrollingLayerRect.top += mAutoScrollY;
7708 nativeScrollLayer(mScrollingLayer,
7709 mScrollingLayerRect.left,
7710 mScrollingLayerRect.top);
7711 invalidate();
7712 }
Cary Clark6f5dfc62010-11-11 13:09:20 -05007713 sendEmptyMessageDelayed(
7714 SCROLL_SELECT_TEXT, SELECT_SCROLL_INTERVAL);
7715 break;
7716 }
Grace Klobac2242f22010-03-05 14:00:26 -08007717 case SWITCH_TO_SHORTPRESS: {
Cary Clarkb8491342010-11-29 16:23:19 -05007718 mInitialHitTestResult = null; // set by updateSelection()
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007719 if (mTouchMode == TOUCH_INIT_MODE) {
Grace Kloba178db412010-05-18 22:22:23 -07007720 if (!getSettings().supportTouchOnly()
7721 && mPreventDefault != PREVENT_DEFAULT_YES) {
Grace Klobac2242f22010-03-05 14:00:26 -08007722 mTouchMode = TOUCH_SHORTPRESS_START_MODE;
7723 updateSelection();
7724 } else {
7725 // set to TOUCH_SHORTPRESS_MODE so that it won't
7726 // trigger double tap any more
7727 mTouchMode = TOUCH_SHORTPRESS_MODE;
7728 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07007729 } else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
7730 mTouchMode = TOUCH_DONE_MODE;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007731 }
7732 break;
7733 }
7734 case SWITCH_TO_LONGPRESS: {
Grace Kloba178db412010-05-18 22:22:23 -07007735 if (getSettings().supportTouchOnly()) {
7736 removeTouchHighlight(false);
7737 }
Grace Klobac2242f22010-03-05 14:00:26 -08007738 if (inFullScreenMode() || mDeferTouchProcess) {
7739 TouchEventData ted = new TouchEventData();
Grace Kloba5f68d6f2009-12-08 18:42:54 -08007740 ted.mAction = WebViewCore.ACTION_LONGPRESS;
Huahui Wue838a422011-01-13 16:03:43 -08007741 ted.mIds = new int[1];
7742 ted.mIds[0] = 0;
Huahui Wu41865f42010-09-02 13:41:41 -07007743 ted.mPoints = new Point[1];
Shimeng (Simon) Wang1e07da32011-01-19 12:02:13 -08007744 ted.mPoints[0] = new Point(viewToContentX(mLastTouchX + mScrollX),
7745 viewToContentY(mLastTouchY + mScrollY));
Huahui Wu88b869a2011-03-17 17:42:12 -07007746 ted.mPointsInView = new Point[1];
7747 ted.mPointsInView[0] = new Point(mLastTouchX, mLastTouchY);
Grace Klobac2242f22010-03-05 14:00:26 -08007748 // metaState for long press is tricky. Should it be the
7749 // state when the press started or when the press was
7750 // released? Or some intermediary key state? For
7751 // simplicity for now, we don't set it.
Ben Murdoch8a032a32010-02-02 18:20:11 +00007752 ted.mMetaState = 0;
Grace Klobac2242f22010-03-05 14:00:26 -08007753 ted.mReprocess = mDeferTouchProcess;
Patrick Scottcfa734a2011-02-22 11:19:02 -05007754 ted.mNativeLayer = nativeScrollableLayer(
7755 ted.mPoints[0].x, ted.mPoints[0].y,
7756 ted.mNativeLayerRect, null);
Adam Powellae9d2642011-03-08 16:00:30 -08007757 ted.mSequence = mTouchEventQueue.nextTouchSequence();
Adam Powell3c534772011-04-04 14:27:12 -07007758 mTouchEventQueue.preQueueTouchEventData(ted);
Grace Kloba5f68d6f2009-12-08 18:42:54 -08007759 mWebViewCore.sendMessage(EventHub.TOUCH_EVENT, ted);
Grace Klobac2242f22010-03-05 14:00:26 -08007760 } else if (mPreventDefault != PREVENT_DEFAULT_YES) {
Grace Kloba6c451b72009-06-25 12:25:30 -07007761 mTouchMode = TOUCH_DONE_MODE;
Grace Klobac2242f22010-03-05 14:00:26 -08007762 performLongClick();
Grace Kloba6c451b72009-06-25 12:25:30 -07007763 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007764 break;
7765 }
Grace Kloba8b97e4b2009-07-28 13:11:38 -07007766 case RELEASE_SINGLE_TAP: {
Grace Klobac2242f22010-03-05 14:00:26 -08007767 doShortPress();
Grace Kloba8b97e4b2009-07-28 13:11:38 -07007768 break;
7769 }
Patrick Scottfa8be1c2011-02-02 14:09:34 -05007770 case SCROLL_TO_MSG_ID: {
7771 // arg1 = animate, arg2 = onlyIfImeIsShowing
7772 // obj = Point(x, y)
7773 if (msg.arg2 == 1) {
Leon Scrogginsfe77eb62011-02-01 11:05:22 -05007774 // This scroll is intended to bring the textfield into
7775 // view, but is only necessary if the IME is showing
7776 InputMethodManager imm = InputMethodManager.peekInstance();
7777 if (imm == null || !imm.isAcceptingText()
7778 || (!imm.isActive(WebView.this) && (!inEditingMode()
7779 || !imm.isActive(mWebTextView)))) {
7780 break;
7781 }
7782 }
Patrick Scottfa8be1c2011-02-02 14:09:34 -05007783 final Point p = (Point) msg.obj;
7784 if (msg.arg1 == 1) {
7785 spawnContentScrollTo(p.x, p.y);
7786 } else {
7787 setContentScrollTo(p.x, p.y);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007788 }
7789 break;
Patrick Scottfa8be1c2011-02-02 14:09:34 -05007790 }
Grace Kloba769ed212010-01-27 10:52:47 -08007791 case UPDATE_ZOOM_RANGE: {
Derek Sollenbergerb983c892010-06-28 08:38:28 -04007792 WebViewCore.ViewState viewState = (WebViewCore.ViewState) msg.obj;
Grace Kloba188bf8d2010-04-07 11:30:19 -07007793 // mScrollX contains the new minPrefWidth
Derek Sollenbergerb983c892010-06-28 08:38:28 -04007794 mZoomManager.updateZoomRange(viewState, getViewWidth(), viewState.mScrollX);
Grace Kloba769ed212010-01-27 10:52:47 -08007795 break;
7796 }
Grace Kloba8abd50b2010-07-08 15:02:14 -07007797 case REPLACE_BASE_CONTENT: {
7798 nativeReplaceBaseContent(msg.arg1);
7799 break;
7800 }
Grace Klobae397a882009-08-06 12:04:14 -07007801 case NEW_PICTURE_MSG_ID: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007802 // called for new content
Derek Sollenberger341e22f2010-06-02 12:34:34 -04007803 final WebViewCore.DrawData draw = (WebViewCore.DrawData) msg.obj;
Derek Sollenbergerb983c892010-06-28 08:38:28 -04007804 WebViewCore.ViewState viewState = draw.mViewState;
7805 boolean isPictureAfterFirstLayout = viewState != null;
Shimeng (Simon) Wang464b6902011-03-16 11:27:44 -07007806 setBaseLayer(draw.mBaseLayer, draw.mInvalRegion,
7807 getSettings().getShowVisualIndicator(),
7808 isPictureAfterFirstLayout);
7809 final Point viewSize = draw.mViewSize;
Derek Sollenbergerb983c892010-06-28 08:38:28 -04007810 if (isPictureAfterFirstLayout) {
Shimeng (Simon) Wang0a4a70d2010-06-30 11:48:05 -07007811 // Reset the last sent data here since dealing with new page.
7812 mLastWidthSent = 0;
Derek Sollenbergerb983c892010-06-28 08:38:28 -04007813 mZoomManager.onFirstLayout(draw);
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08007814 if (!mDrawHistory) {
Patrick Scottfa8be1c2011-02-02 14:09:34 -05007815 // Do not send the scroll event for this particular
7816 // scroll message. Note that a scroll event may
7817 // still be fired if the user scrolls before the
7818 // message can be handled.
7819 mSendScrollEvent = false;
Derek Sollenbergerb983c892010-06-28 08:38:28 -04007820 setContentScrollTo(viewState.mScrollX, viewState.mScrollY);
Patrick Scottfa8be1c2011-02-02 14:09:34 -05007821 mSendScrollEvent = true;
7822
Shimeng (Simon) Wang1c02c6a2010-03-04 18:06:49 -08007823 // As we are on a new page, remove the WebTextView. This
7824 // is necessary for page loads driven by webkit, and in
7825 // particular when the user was on a password field, so
7826 // the WebTextView was visible.
Leon Scroggins2aed7762010-08-13 17:11:42 -04007827 clearTextEntry();
Mike Reed8b302092009-11-12 12:50:20 -05007828 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007829 }
Derek Sollenberger341e22f2010-06-02 12:34:34 -04007830
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007831 // We update the layout (i.e. request a layout from the
7832 // view system) if the last view size that we sent to
7833 // WebCore matches the view size of the picture we just
7834 // received in the fixed dimension.
7835 final boolean updateLayout = viewSize.x == mLastWidthSent
7836 && viewSize.y == mLastHeightSent;
Shimeng (Simon) Wangc5661852011-02-10 13:32:53 -08007837 // Don't send scroll event for picture coming from webkit,
7838 // since the new picture may cause a scroll event to override
7839 // the saved history scroll position.
7840 mSendScrollEvent = false;
Derek Sollenberger7e6bf6f2010-10-28 09:33:47 -04007841 recordNewContentSize(draw.mContentSize.x,
7842 draw.mContentSize.y, updateLayout);
Shimeng (Simon) Wangc5661852011-02-10 13:32:53 -08007843 mSendScrollEvent = true;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04007844 if (DebugFlags.WEB_VIEW) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007845 Rect b = draw.mInvalRegion.getBounds();
7846 Log.v(LOGTAG, "NEW_PICTURE_MSG_ID {" +
7847 b.left+","+b.top+","+b.right+","+b.bottom+"}");
7848 }
Mike Reede9e86b82009-09-15 11:26:53 -04007849 invalidateContentRect(draw.mInvalRegion.getBounds());
Derek Sollenberger341e22f2010-06-02 12:34:34 -04007850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007851 if (mPictureListener != null) {
7852 mPictureListener.onNewPicture(WebView.this, capturePicture());
7853 }
Derek Sollenberger341e22f2010-06-02 12:34:34 -04007854
7855 // update the zoom information based on the new picture
7856 mZoomManager.onNewPicture(draw);
7857
Cary Clark5da9aeb2009-10-06 17:40:53 -04007858 if (draw.mFocusSizeChanged && inEditingMode()) {
7859 mFocusSizeChanged = true;
7860 }
Derek Sollenbergerb983c892010-06-28 08:38:28 -04007861 if (isPictureAfterFirstLayout) {
Grace Kloba9a67c822009-12-20 11:33:58 -08007862 mViewManager.postReadyToDrawAll();
7863 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007864 break;
Grace Klobae397a882009-08-06 12:04:14 -07007865 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007866 case WEBCORE_INITIALIZED_MSG_ID:
7867 // nativeCreate sets mNativeClass to a non-zero value
7868 nativeCreate(msg.arg1);
7869 break;
7870 case UPDATE_TEXTFIELD_TEXT_MSG_ID:
7871 // Make sure that the textfield is currently focused
Cary Clarkd6982c92009-05-29 11:02:22 -04007872 // and representing the same node as the pointer.
7873 if (inEditingMode() &&
Leon Scrogginsd3465f62009-06-02 10:57:54 -04007874 mWebTextView.isSameTextField(msg.arg1)) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007875 if (msg.getData().getBoolean("password")) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04007876 Spannable text = (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007877 int start = Selection.getSelectionStart(text);
7878 int end = Selection.getSelectionEnd(text);
Leon Scrogginsd3465f62009-06-02 10:57:54 -04007879 mWebTextView.setInPassword(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007880 // Restore the selection, which may have been
7881 // ruined by setInPassword.
Leon Scrogginsd3465f62009-06-02 10:57:54 -04007882 Spannable pword =
7883 (Spannable) mWebTextView.getText();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007884 Selection.setSelection(pword, start, end);
7885 // If the text entry has created more events, ignore
7886 // this one.
7887 } else if (msg.arg2 == mTextGeneration) {
Leon Scrogginsef7af282010-11-11 14:20:06 -05007888 String text = (String) msg.obj;
7889 if (null == text) {
7890 text = "";
7891 }
7892 mWebTextView.setTextAndKeepSelection(text);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007893 }
7894 }
7895 break;
Leon Scrogginsb4157792010-03-18 12:42:33 -04007896 case REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID:
7897 displaySoftKeyboard(true);
Leon Scroggins III901a7c22010-04-20 15:14:02 -04007898 // fall through to UPDATE_TEXT_SELECTION_MSG_ID
Leon Scroggins6679f2f2009-08-12 18:48:10 -04007899 case UPDATE_TEXT_SELECTION_MSG_ID:
Leon Scrogginsb4157792010-03-18 12:42:33 -04007900 updateTextSelectionFromMessage(msg.arg1, msg.arg2,
7901 (WebViewCore.TextSelectionData) msg.obj);
Leon Scroggins6679f2f2009-08-12 18:48:10 -04007902 break;
Leon Scrogginsf2e17a82010-09-24 15:58:50 -04007903 case FORM_DID_BLUR:
7904 if (inEditingMode()
7905 && mWebTextView.isSameTextField(msg.arg1)) {
7906 hideSoftKeyboard();
7907 }
7908 break;
Leon Scroggins3a503392010-01-06 17:04:38 -05007909 case RETURN_LABEL:
7910 if (inEditingMode()
7911 && mWebTextView.isSameTextField(msg.arg1)) {
7912 mWebTextView.setHint((String) msg.obj);
7913 InputMethodManager imm
7914 = InputMethodManager.peekInstance();
7915 // The hint is propagated to the IME in
7916 // onCreateInputConnection. If the IME is already
7917 // active, restart it so that its hint text is updated.
7918 if (imm != null && imm.isActive(mWebTextView)) {
7919 imm.restartInput(mWebTextView);
7920 }
7921 }
7922 break;
Leon Scroggins9ab32b62010-05-03 14:19:50 +01007923 case UNHANDLED_NAV_KEY:
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05007924 navHandledKey(msg.arg1, 1, false, 0);
Cary Clark215b72c2009-06-26 14:38:43 -04007925 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007926 case UPDATE_TEXT_ENTRY_MSG_ID:
Cary Clarkd6982c92009-05-29 11:02:22 -04007927 // this is sent after finishing resize in WebViewCore. Make
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007928 // sure the text edit box is still on the screen.
Cary Clarkd6982c92009-05-29 11:02:22 -04007929 if (inEditingMode() && nativeCursorIsTextInput()) {
Leon Scroggins4890feb2009-07-02 10:37:10 -04007930 rebuildWebTextView();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007931 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007932 break;
Cary Clark243ea062009-06-25 10:49:32 -04007933 case CLEAR_TEXT_ENTRY:
Leon Scroggins2aed7762010-08-13 17:11:42 -04007934 clearTextEntry();
Cary Clark243ea062009-06-25 10:49:32 -04007935 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007936 case INVAL_RECT_MSG_ID: {
7937 Rect r = (Rect)msg.obj;
7938 if (r == null) {
7939 invalidate();
7940 } else {
7941 // we need to scale r from content into view coords,
7942 // which viewInvalidate() does for us
7943 viewInvalidate(r.left, r.top, r.right, r.bottom);
7944 }
7945 break;
7946 }
Leon Scrogginsd3465f62009-06-02 10:57:54 -04007947 case REQUEST_FORM_DATA:
Cary Clarkded054c2009-06-15 10:26:08 -04007948 AutoCompleteAdapter adapter = (AutoCompleteAdapter) msg.obj;
Leon Scrogginsd3465f62009-06-02 10:57:54 -04007949 if (mWebTextView.isSameTextField(msg.arg1)) {
Leon Scrogginsd3465f62009-06-02 10:57:54 -04007950 mWebTextView.setAdapterCustom(adapter);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007951 }
7952 break;
Grace Kloba96949ef2010-01-25 09:53:01 -08007953 case RESUME_WEBCORE_PRIORITY:
Grace Klobaa7bc87c2010-01-29 14:56:25 -08007954 WebViewCore.resumePriority();
Grace Kloba524aab572010-04-07 11:12:52 -07007955 WebViewCore.resumeUpdatePicture(mWebViewCore);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007956 break;
7957
Leon Scrogginse3225672009-06-03 15:53:13 -04007958 case LONG_PRESS_CENTER:
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007959 // as this is shared by keydown and trackballdown, reset all
7960 // the states
Leon Scrogginse3225672009-06-03 15:53:13 -04007961 mGotCenterDown = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007962 mTrackballDown = false;
Grace Kloba98e6fcf2010-01-27 15:20:30 -08007963 performLongClick();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007964 break;
7965
7966 case WEBCORE_NEED_TOUCH_EVENTS:
7967 mForwardTouchEvents = (msg.arg1 != 0);
7968 break;
7969
7970 case PREVENT_TOUCH_ID:
Grace Klobac2242f22010-03-05 14:00:26 -08007971 if (inFullScreenMode()) {
7972 break;
7973 }
Adam Powell4fb35d42011-03-03 17:54:55 -08007974 TouchEventData ted = (TouchEventData) msg.obj;
Adam Powellae9d2642011-03-08 16:00:30 -08007975
Adam Powellbaa33802011-03-25 13:58:19 -07007976 if (mTouchEventQueue.enqueueTouchEvent(ted)) {
7977 // WebCore is responding to us; remove pending timeout.
7978 // It will be re-posted when needed.
7979 removeMessages(PREVENT_DEFAULT_TIMEOUT);
7980 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08007981 break;
7982
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04007983 case REQUEST_KEYBOARD:
7984 if (msg.arg1 == 0) {
7985 hideSoftKeyboard();
7986 } else {
Leon Scrogginsb4157792010-03-18 12:42:33 -04007987 displaySoftKeyboard(false);
Derek Sollenberger4c41e8d2009-06-29 13:49:27 -04007988 }
7989 break;
7990
Leon Scroggins5de63892009-10-29 09:48:43 -04007991 case FIND_AGAIN:
7992 // Ignore if find has been dismissed.
Leon Scrogginsfe026bd2010-08-24 14:16:09 -04007993 if (mFindIsUp && mFindCallback != null) {
7994 mFindCallback.findAll();
Leon Scroggins5de63892009-10-29 09:48:43 -04007995 }
7996 break;
7997
Cary Clark25415e22009-10-12 13:41:28 -04007998 case DRAG_HELD_MOTIONLESS:
7999 mHeldMotionless = MOTIONLESS_TRUE;
8000 invalidate();
8001 // fall through to keep scrollbars awake
8002
8003 case AWAKEN_SCROLL_BARS:
8004 if (mTouchMode == TOUCH_DRAG_MODE
8005 && mHeldMotionless == MOTIONLESS_TRUE) {
8006 awakenScrollBars(ViewConfiguration
8007 .getScrollDefaultDelay(), false);
8008 mPrivateHandler.sendMessageDelayed(mPrivateHandler
8009 .obtainMessage(AWAKEN_SCROLL_BARS),
8010 ViewConfiguration.getScrollDefaultDelay());
8011 }
8012 break;
Cary Clark1cb97ee2009-12-11 12:10:36 -05008013
8014 case DO_MOTION_UP:
Cary Clarkbad0c542010-01-11 14:58:21 -05008015 doMotionUp(msg.arg1, msg.arg2);
Cary Clark1cb97ee2009-12-11 12:10:36 -05008016 break;
8017
Derek Sollenbergerf3196cd2011-01-27 17:33:14 -05008018 case SCREEN_ON:
8019 setKeepScreenOn(msg.arg1 == 1);
8020 break;
8021
Nicolas Roard0e778a12011-03-11 14:29:05 -08008022 case ENTER_FULLSCREEN_VIDEO:
8023 int layerId = msg.arg1;
Teng-Hui Zhu10ab6542011-03-16 16:42:32 -07008024
8025 String url = (String) msg.obj;
8026 if (mHTML5VideoViewProxy != null) {
8027 mHTML5VideoViewProxy.enterFullScreenVideo(layerId, url);
8028 }
Nicolas Roard0e778a12011-03-11 14:29:05 -08008029 break;
8030
Grace Kloba3a0def22010-01-23 21:11:54 -08008031 case SHOW_FULLSCREEN: {
Derek Sollenbergerc28ff442010-03-09 17:38:49 -05008032 View view = (View) msg.obj;
8033 int npp = msg.arg1;
Grace Kloba11438c32009-12-16 11:39:12 -08008034
Derek Sollenbergerfa89c502010-10-01 11:19:36 -04008035 if (inFullScreenMode()) {
Derek Sollenbergerc28ff442010-03-09 17:38:49 -05008036 Log.w(LOGTAG, "Should not have another full screen.");
Derek Sollenbergerfa89c502010-10-01 11:19:36 -04008037 dismissFullScreenMode();
Derek Sollenbergerc28ff442010-03-09 17:38:49 -05008038 }
8039 mFullScreenHolder = new PluginFullScreenHolder(WebView.this, npp);
8040 mFullScreenHolder.setContentView(view);
8041 mFullScreenHolder.setCancelable(false);
8042 mFullScreenHolder.setCanceledOnTouchOutside(false);
8043 mFullScreenHolder.show();
8044
8045 break;
8046 }
Grace Kloba11438c32009-12-16 11:39:12 -08008047 case HIDE_FULLSCREEN:
Derek Sollenbergerfa89c502010-10-01 11:19:36 -04008048 dismissFullScreenMode();
Grace Kloba11438c32009-12-16 11:39:12 -08008049 break;
8050
Leon Scrogginse26efa32009-12-15 16:38:45 -05008051 case DOM_FOCUS_CHANGED:
8052 if (inEditingMode()) {
8053 nativeClearCursor();
8054 rebuildWebTextView();
8055 }
8056 break;
8057
Grace Kloba3a0def22010-01-23 21:11:54 -08008058 case SHOW_RECT_MSG_ID: {
8059 WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
8060 int x = mScrollX;
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08008061 int left = contentToViewX(data.mLeft);
Grace Kloba3a0def22010-01-23 21:11:54 -08008062 int width = contentToViewDimension(data.mWidth);
8063 int maxWidth = contentToViewDimension(data.mContentWidth);
8064 int viewWidth = getViewWidth();
8065 if (width < viewWidth) {
8066 // center align
8067 x += left + width / 2 - mScrollX - viewWidth / 2;
8068 } else {
8069 x += (int) (left + data.mXPercentInDoc * width
8070 - mScrollX - data.mXPercentInView * viewWidth);
8071 }
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08008072 if (DebugFlags.WEB_VIEW) {
8073 Log.v(LOGTAG, "showRectMsg=(left=" + left + ",width=" +
8074 width + ",maxWidth=" + maxWidth +
8075 ",viewWidth=" + viewWidth + ",x="
8076 + x + ",xPercentInDoc=" + data.mXPercentInDoc +
8077 ",xPercentInView=" + data.mXPercentInView+ ")");
8078 }
Grace Kloba3a0def22010-01-23 21:11:54 -08008079 // use the passing content width to cap x as the current
8080 // mContentWidth may not be updated yet
8081 x = Math.max(0,
8082 (Math.min(maxWidth, x + viewWidth)) - viewWidth);
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08008083 int top = contentToViewY(data.mTop);
Grace Kloba3a0def22010-01-23 21:11:54 -08008084 int height = contentToViewDimension(data.mHeight);
8085 int maxHeight = contentToViewDimension(data.mContentHeight);
8086 int viewHeight = getViewHeight();
Shimeng (Simon) Wang8a7ac8d2010-01-28 15:05:51 -08008087 int y = (int) (top + data.mYPercentInDoc * height -
8088 data.mYPercentInView * viewHeight);
8089 if (DebugFlags.WEB_VIEW) {
8090 Log.v(LOGTAG, "showRectMsg=(top=" + top + ",height=" +
8091 height + ",maxHeight=" + maxHeight +
8092 ",viewHeight=" + viewHeight + ",y="
8093 + y + ",yPercentInDoc=" + data.mYPercentInDoc +
8094 ",yPercentInView=" + data.mYPercentInView+ ")");
Grace Kloba3a0def22010-01-23 21:11:54 -08008095 }
8096 // use the passing content height to cap y as the current
8097 // mContentHeight may not be updated yet
8098 y = Math.max(0,
8099 (Math.min(maxHeight, y + viewHeight) - viewHeight));
Shimeng (Simon) Wangb7f17d42010-02-08 15:17:21 -08008100 // We need to take into account the visible title height
8101 // when scrolling since y is an absolute view position.
8102 y = Math.max(0, y - getVisibleTitleHeight());
Grace Kloba3a0def22010-01-23 21:11:54 -08008103 scrollTo(x, y);
8104 }
8105 break;
8106
Grace Klobae8300a12010-03-12 13:32:55 -08008107 case CENTER_FIT_RECT:
Derek Sollenbergerdd1173b2011-01-18 11:11:28 -05008108 centerFitRect((Rect)msg.obj);
Grace Klobae8300a12010-03-12 13:32:55 -08008109 break;
8110
Grace Kloba50004bc2010-04-13 22:58:51 -07008111 case SET_SCROLLBAR_MODES:
8112 mHorizontalScrollBarMode = msg.arg1;
8113 mVerticalScrollBarMode = msg.arg2;
8114 break;
8115
Svetoslav Ganovda355512010-05-12 22:04:44 -07008116 case SELECTION_STRING_CHANGED:
8117 if (mAccessibilityInjector != null) {
8118 String selectionString = (String) msg.obj;
8119 mAccessibilityInjector.onSelectionStringChange(selectionString);
8120 }
8121 break;
8122
Grace Kloba178db412010-05-18 22:22:23 -07008123 case SET_TOUCH_HIGHLIGHT_RECTS:
8124 invalidate(mTouchHighlightRegion.getBounds());
8125 mTouchHighlightRegion.setEmpty();
8126 if (msg.obj != null) {
8127 ArrayList<Rect> rects = (ArrayList<Rect>) msg.obj;
8128 for (Rect rect : rects) {
8129 Rect viewRect = contentToViewRect(rect);
8130 // some sites, like stories in nytimes.com, set
8131 // mouse event handler in the top div. It is not
8132 // user friendly to highlight the div if it covers
8133 // more than half of the screen.
8134 if (viewRect.width() < getWidth() >> 1
8135 || viewRect.height() < getHeight() >> 1) {
8136 mTouchHighlightRegion.union(viewRect);
8137 invalidate(viewRect);
8138 } else {
8139 Log.w(LOGTAG, "Skip the huge selection rect:"
8140 + viewRect);
8141 }
8142 }
8143 }
8144 break;
8145
Elliott Slaughterb48fdbe2010-06-30 11:39:52 -07008146 case SAVE_WEBARCHIVE_FINISHED:
8147 SaveWebArchiveMessage saveMessage = (SaveWebArchiveMessage)msg.obj;
8148 if (saveMessage.mCallback != null) {
8149 saveMessage.mCallback.onReceiveValue(saveMessage.mResultFile);
8150 }
8151 break;
8152
Ben Murdoch62275a42010-09-07 11:27:28 +01008153 case SET_AUTOFILLABLE:
Ben Murdochdb8d19c2010-10-29 11:44:17 +01008154 mAutoFillData = (WebViewCore.AutoFillData) msg.obj;
Ben Murdoch62275a42010-09-07 11:27:28 +01008155 if (mWebTextView != null) {
Ben Murdochdb8d19c2010-10-29 11:44:17 +01008156 mWebTextView.setAutoFillable(mAutoFillData.getQueryId());
Ben Murdoch62275a42010-09-07 11:27:28 +01008157 rebuildWebTextView();
8158 }
8159 break;
8160
Ben Murdoch961d55f2010-12-02 13:58:24 +00008161 case AUTOFILL_COMPLETE:
8162 if (mWebTextView != null) {
8163 // Clear the WebTextView adapter when AutoFill finishes
8164 // so that the drop down gets cleared.
8165 mWebTextView.setAdapterCustom(null);
8166 }
8167 break;
8168
Svetoslav Ganov9504f572011-01-14 11:38:17 -08008169 case SELECT_AT:
8170 nativeSelectAt(msg.arg1, msg.arg2);
8171 break;
8172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008173 default:
8174 super.handleMessage(msg);
8175 break;
8176 }
8177 }
8178 }
8179
Leon Scrogginsb4157792010-03-18 12:42:33 -04008180 /**
8181 * Used when receiving messages for REQUEST_KEYBOARD_WITH_SELECTION_MSG_ID
8182 * and UPDATE_TEXT_SELECTION_MSG_ID. Update the selection of WebTextView.
8183 */
8184 private void updateTextSelectionFromMessage(int nodePointer,
8185 int textGeneration, WebViewCore.TextSelectionData data) {
8186 if (inEditingMode()
8187 && mWebTextView.isSameTextField(nodePointer)
8188 && textGeneration == mTextGeneration) {
8189 mWebTextView.setSelectionFromWebKit(data.mStart, data.mEnd);
8190 }
8191 }
8192
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008193 // Class used to use a dropdown for a <select> element
8194 private class InvokeListBox implements Runnable {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008195 // Whether the listbox allows multiple selection.
8196 private boolean mMultiple;
8197 // Passed in to a list with multiple selection to tell
8198 // which items are selected.
8199 private int[] mSelectedArray;
Cary Clarkd6982c92009-05-29 11:02:22 -04008200 // Passed in to a list with single selection to tell
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008201 // where the initial selection is.
8202 private int mSelection;
8203
8204 private Container[] mContainers;
8205
8206 // Need these to provide stable ids to my ArrayAdapter,
8207 // which normally does not have stable ids. (Bug 1250098)
8208 private class Container extends Object {
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008209 /**
8210 * Possible values for mEnabled. Keep in sync with OptionStatus in
8211 * WebViewCore.cpp
8212 */
8213 final static int OPTGROUP = -1;
8214 final static int OPTION_DISABLED = 0;
8215 final static int OPTION_ENABLED = 1;
8216
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008217 String mString;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008218 int mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008219 int mId;
8220
Gilles Debunne0e7d652d2011-02-22 15:26:14 -08008221 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008222 public String toString() {
8223 return mString;
8224 }
8225 }
8226
8227 /**
Cary Clarkd6982c92009-05-29 11:02:22 -04008228 * Subclass ArrayAdapter so we can disable OptionGroupLabels,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008229 * and allow filtering.
8230 */
8231 private class MyArrayListAdapter extends ArrayAdapter<Container> {
Leon Scrogginsd529bfd2011-02-15 11:10:38 -05008232 public MyArrayListAdapter() {
8233 super(mContext,
8234 mMultiple ? com.android.internal.R.layout.select_dialog_multichoice :
8235 com.android.internal.R.layout.webview_select_singlechoice,
8236 mContainers);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008237 }
8238
8239 @Override
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008240 public View getView(int position, View convertView,
8241 ViewGroup parent) {
8242 // Always pass in null so that we will get a new CheckedTextView
8243 // Otherwise, an item which was previously used as an <optgroup>
8244 // element (i.e. has no check), could get used as an <option>
8245 // element, which needs a checkbox/radio, but it would not have
8246 // one.
8247 convertView = super.getView(position, null, parent);
8248 Container c = item(position);
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -04008249 if (c != null && Container.OPTION_ENABLED != c.mEnabled) {
8250 // ListView does not draw dividers between disabled and
8251 // enabled elements. Use a LinearLayout to provide dividers
8252 LinearLayout layout = new LinearLayout(mContext);
8253 layout.setOrientation(LinearLayout.VERTICAL);
8254 if (position > 0) {
8255 View dividerTop = new View(mContext);
8256 dividerTop.setBackgroundResource(
8257 android.R.drawable.divider_horizontal_bright);
8258 layout.addView(dividerTop);
8259 }
8260
8261 if (Container.OPTGROUP == c.mEnabled) {
Leon Scrogginsd529bfd2011-02-15 11:10:38 -05008262 // Currently select_dialog_multichoice uses CheckedTextViews.
8263 // If that changes, the class cast will no longer be valid.
8264 if (mMultiple) {
8265 Assert.assertTrue(convertView instanceof CheckedTextView);
8266 ((CheckedTextView) convertView).setCheckMarkDrawable(null);
8267 }
Leon Scrogginsf1e1fb32009-10-21 13:39:33 -04008268 } else {
8269 // c.mEnabled == Container.OPTION_DISABLED
8270 // Draw the disabled element in a disabled state.
8271 convertView.setEnabled(false);
8272 }
8273
8274 layout.addView(convertView);
8275 if (position < getCount() - 1) {
8276 View dividerBottom = new View(mContext);
8277 dividerBottom.setBackgroundResource(
8278 android.R.drawable.divider_horizontal_bright);
8279 layout.addView(dividerBottom);
8280 }
8281 return layout;
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008282 }
8283 return convertView;
8284 }
8285
8286 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008287 public boolean hasStableIds() {
Leon Scroggins3667ce42009-05-13 15:58:03 -04008288 // AdapterView's onChanged method uses this to determine whether
8289 // to restore the old state. Return false so that the old (out
8290 // of date) state does not replace the new, valid state.
8291 return false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008292 }
8293
8294 private Container item(int position) {
8295 if (position < 0 || position >= getCount()) {
8296 return null;
8297 }
8298 return (Container) getItem(position);
8299 }
8300
8301 @Override
8302 public long getItemId(int position) {
8303 Container item = item(position);
8304 if (item == null) {
8305 return -1;
8306 }
8307 return item.mId;
8308 }
8309
8310 @Override
8311 public boolean areAllItemsEnabled() {
8312 return false;
8313 }
8314
8315 @Override
8316 public boolean isEnabled(int position) {
8317 Container item = item(position);
8318 if (item == null) {
8319 return false;
8320 }
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008321 return Container.OPTION_ENABLED == item.mEnabled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008322 }
8323 }
8324
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008325 private InvokeListBox(String[] array, int[] enabled, int[] selected) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008326 mMultiple = true;
8327 mSelectedArray = selected;
8328
8329 int length = array.length;
8330 mContainers = new Container[length];
8331 for (int i = 0; i < length; i++) {
8332 mContainers[i] = new Container();
8333 mContainers[i].mString = array[i];
8334 mContainers[i].mEnabled = enabled[i];
8335 mContainers[i].mId = i;
8336 }
8337 }
8338
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008339 private InvokeListBox(String[] array, int[] enabled, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008340 mSelection = selection;
8341 mMultiple = false;
8342
8343 int length = array.length;
8344 mContainers = new Container[length];
8345 for (int i = 0; i < length; i++) {
8346 mContainers[i] = new Container();
8347 mContainers[i].mString = array[i];
8348 mContainers[i].mEnabled = enabled[i];
8349 mContainers[i].mId = i;
8350 }
8351 }
8352
Leon Scroggins3667ce42009-05-13 15:58:03 -04008353 /*
8354 * Whenever the data set changes due to filtering, this class ensures
8355 * that the checked item remains checked.
8356 */
8357 private class SingleDataSetObserver extends DataSetObserver {
8358 private long mCheckedId;
8359 private ListView mListView;
8360 private Adapter mAdapter;
8361
8362 /*
8363 * Create a new observer.
8364 * @param id The ID of the item to keep checked.
8365 * @param l ListView for getting and clearing the checked states
8366 * @param a Adapter for getting the IDs
8367 */
8368 public SingleDataSetObserver(long id, ListView l, Adapter a) {
8369 mCheckedId = id;
8370 mListView = l;
8371 mAdapter = a;
8372 }
8373
Leon Scrogginsd529bfd2011-02-15 11:10:38 -05008374 @Override
Leon Scroggins3667ce42009-05-13 15:58:03 -04008375 public void onChanged() {
8376 // The filter may have changed which item is checked. Find the
8377 // item that the ListView thinks is checked.
8378 int position = mListView.getCheckedItemPosition();
8379 long id = mAdapter.getItemId(position);
8380 if (mCheckedId != id) {
8381 // Clear the ListView's idea of the checked item, since
8382 // it is incorrect
8383 mListView.clearChoices();
8384 // Search for mCheckedId. If it is in the filtered list,
8385 // mark it as checked
8386 int count = mAdapter.getCount();
8387 for (int i = 0; i < count; i++) {
8388 if (mAdapter.getItemId(i) == mCheckedId) {
8389 mListView.setItemChecked(i, true);
8390 break;
8391 }
8392 }
8393 }
8394 }
Leon Scroggins3667ce42009-05-13 15:58:03 -04008395 }
8396
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008397 public void run() {
8398 final ListView listView = (ListView) LayoutInflater.from(mContext)
8399 .inflate(com.android.internal.R.layout.select_dialog, null);
Leon Scrogginsd529bfd2011-02-15 11:10:38 -05008400 final MyArrayListAdapter adapter = new MyArrayListAdapter();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008401 AlertDialog.Builder b = new AlertDialog.Builder(mContext)
8402 .setView(listView).setCancelable(true)
8403 .setInverseBackgroundForced(true);
Cary Clarkd6982c92009-05-29 11:02:22 -04008404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008405 if (mMultiple) {
8406 b.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
8407 public void onClick(DialogInterface dialog, int which) {
8408 mWebViewCore.sendMessage(
Cary Clarkd6982c92009-05-29 11:02:22 -04008409 EventHub.LISTBOX_CHOICES,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008410 adapter.getCount(), 0,
8411 listView.getCheckedItemPositions());
8412 }});
Leon Scroggins238ddbb2009-04-02 10:47:53 -07008413 b.setNegativeButton(android.R.string.cancel,
8414 new DialogInterface.OnClickListener() {
8415 public void onClick(DialogInterface dialog, int which) {
8416 mWebViewCore.sendMessage(
8417 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
8418 }});
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008419 }
Mattias Falk0ae2ec82010-09-16 16:24:46 +02008420 mListBoxDialog = b.create();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008421 listView.setAdapter(adapter);
8422 listView.setFocusableInTouchMode(true);
8423 // There is a bug (1250103) where the checks in a ListView with
8424 // multiple items selected are associated with the positions, not
Cary Clarkd6982c92009-05-29 11:02:22 -04008425 // the ids, so the items do not properly retain their checks when
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008426 // filtered. Do not allow filtering on multiple lists until
8427 // that bug is fixed.
Cary Clarkd6982c92009-05-29 11:02:22 -04008428
Leon Scroggins3667ce42009-05-13 15:58:03 -04008429 listView.setTextFilterEnabled(!mMultiple);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008430 if (mMultiple) {
8431 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
8432 int length = mSelectedArray.length;
8433 for (int i = 0; i < length; i++) {
8434 listView.setItemChecked(mSelectedArray[i], true);
8435 }
8436 } else {
8437 listView.setOnItemClickListener(new OnItemClickListener() {
Leon Scrogginsd529bfd2011-02-15 11:10:38 -05008438 public void onItemClick(AdapterView<?> parent, View v,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008439 int position, long id) {
Leon Scroggins244d2d42010-12-06 16:29:38 -05008440 // Rather than sending the message right away, send it
8441 // after the page regains focus.
8442 mListBoxMessage = Message.obtain(null,
8443 EventHub.SINGLE_LISTBOX_CHOICE, (int) id, 0);
Mattias Falk0ae2ec82010-09-16 16:24:46 +02008444 mListBoxDialog.dismiss();
8445 mListBoxDialog = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008446 }
8447 });
8448 if (mSelection != -1) {
8449 listView.setSelection(mSelection);
8450 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
8451 listView.setItemChecked(mSelection, true);
Leon Scroggins3667ce42009-05-13 15:58:03 -04008452 DataSetObserver observer = new SingleDataSetObserver(
8453 adapter.getItemId(mSelection), listView, adapter);
8454 adapter.registerDataSetObserver(observer);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008455 }
8456 }
Mattias Falk0ae2ec82010-09-16 16:24:46 +02008457 mListBoxDialog.setOnCancelListener(new DialogInterface.OnCancelListener() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008458 public void onCancel(DialogInterface dialog) {
8459 mWebViewCore.sendMessage(
8460 EventHub.SINGLE_LISTBOX_CHOICE, -2, 0);
Mattias Falk0ae2ec82010-09-16 16:24:46 +02008461 mListBoxDialog = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008462 }
8463 });
Mattias Falk0ae2ec82010-09-16 16:24:46 +02008464 mListBoxDialog.show();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008465 }
8466 }
8467
Leon Scroggins244d2d42010-12-06 16:29:38 -05008468 private Message mListBoxMessage;
8469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008470 /*
8471 * Request a dropdown menu for a listbox with multiple selection.
8472 *
8473 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008474 * @param enabledArray State for each element in the list. See static
8475 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008476 * @param selectedArray Which positions are initally selected.
8477 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008478 void requestListBox(String[] array, int[] enabledArray, int[]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008479 selectedArray) {
8480 mPrivateHandler.post(
8481 new InvokeListBox(array, enabledArray, selectedArray));
8482 }
8483
8484 /*
8485 * Request a dropdown menu for a listbox with single selection or a single
8486 * <select> element.
8487 *
8488 * @param array Labels for the listbox.
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008489 * @param enabledArray State for each element in the list. See static
8490 * integers in Container class.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008491 * @param selection Which position is initally selected.
8492 */
Leon Scrogginsa8da1732009-10-19 19:04:30 -04008493 void requestListBox(String[] array, int[] enabledArray, int selection) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008494 mPrivateHandler.post(
8495 new InvokeListBox(array, enabledArray, selection));
8496 }
8497
8498 // called by JNI
Leon Scroggins47fabbf2009-12-08 16:57:26 -05008499 private void sendMoveFocus(int frame, int node) {
8500 mWebViewCore.sendMessage(EventHub.SET_MOVE_FOCUS,
8501 new WebViewCore.CursorData(frame, node, 0, 0));
8502 }
8503
8504 // called by JNI
Cary Clarkd6982c92009-05-29 11:02:22 -04008505 private void sendMoveMouse(int frame, int node, int x, int y) {
8506 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE,
8507 new WebViewCore.CursorData(frame, node, x, y));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008508 }
8509
Leon Scroggins0658e8f2009-06-26 14:09:09 -04008510 /*
8511 * Send a mouse move event to the webcore thread.
8512 *
Leon Scrogginsb6112132011-02-24 12:35:50 -05008513 * @param removeFocus Pass true to remove the WebTextView, if present.
8514 * @param stopPaintingCaret Stop drawing the blinking caret if true.
Leon Scroggins0658e8f2009-06-26 14:09:09 -04008515 * called by JNI
8516 */
Leon Scrogginsb6112132011-02-24 12:35:50 -05008517 @SuppressWarnings("unused")
8518 private void sendMoveMouseIfLatest(boolean removeFocus, boolean stopPaintingCaret) {
Leon Scroggins0658e8f2009-06-26 14:09:09 -04008519 if (removeFocus) {
Leon Scroggins2aed7762010-08-13 17:11:42 -04008520 clearTextEntry();
Cary Clark19436562009-06-04 16:25:07 -04008521 }
Leon Scroggins0658e8f2009-06-26 14:09:09 -04008522 mWebViewCore.sendMessage(EventHub.SET_MOVE_MOUSE_IF_LATEST,
Leon Scrogginsb6112132011-02-24 12:35:50 -05008523 stopPaintingCaret ? 1 : 0, 0,
Leon Scroggins0658e8f2009-06-26 14:09:09 -04008524 cursorData());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008525 }
8526
Leon Scrogginsd5188652011-01-19 17:01:55 -05008527 /**
8528 * Called by JNI to send a message to the webcore thread that the user
8529 * touched the webpage.
8530 * @param touchGeneration Generation number of the touch, to ignore touches
8531 * after a new one has been generated.
8532 * @param frame Pointer to the frame holding the node that was touched.
8533 * @param node Pointer to the node touched.
8534 * @param x x-position of the touch.
8535 * @param y y-position of the touch.
Leon Scrogginsd5188652011-01-19 17:01:55 -05008536 */
Cary Clarkd6982c92009-05-29 11:02:22 -04008537 private void sendMotionUp(int touchGeneration,
Leon Scroggins22e883d2011-01-31 10:54:19 -05008538 int frame, int node, int x, int y) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008539 WebViewCore.TouchUpData touchUpData = new WebViewCore.TouchUpData();
8540 touchUpData.mMoveGeneration = touchGeneration;
Cary Clarkd6982c92009-05-29 11:02:22 -04008541 touchUpData.mFrame = frame;
8542 touchUpData.mNode = node;
8543 touchUpData.mX = x;
8544 touchUpData.mY = y;
Patrick Scottcfa734a2011-02-22 11:19:02 -05008545 touchUpData.mNativeLayer = nativeScrollableLayer(
8546 x, y, touchUpData.mNativeLayerRect, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008547 mWebViewCore.sendMessage(EventHub.TOUCH_UP, touchUpData);
8548 }
8549
8550
8551 private int getScaledMaxXScroll() {
8552 int width;
8553 if (mHeightCanMeasure == false) {
8554 width = getViewWidth() / 4;
8555 } else {
8556 Rect visRect = new Rect();
8557 calcOurVisibleRect(visRect);
8558 width = visRect.width() / 2;
8559 }
8560 // FIXME the divisor should be retrieved from somewhere
Leon Scroggins0236e672009-09-02 21:12:08 -04008561 return viewToContentX(width);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008562 }
8563
8564 private int getScaledMaxYScroll() {
8565 int height;
8566 if (mHeightCanMeasure == false) {
8567 height = getViewHeight() / 4;
8568 } else {
8569 Rect visRect = new Rect();
8570 calcOurVisibleRect(visRect);
8571 height = visRect.height() / 2;
8572 }
8573 // FIXME the divisor should be retrieved from somewhere
8574 // the closest thing today is hard-coded into ScrollView.java
8575 // (from ScrollView.java, line 363) int maxJump = height/2;
Derek Sollenbergerbffa8512010-06-10 14:24:03 -04008576 return Math.round(height * mZoomManager.getInvScale());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008577 }
8578
8579 /**
8580 * Called by JNI to invalidate view
8581 */
8582 private void viewInvalidate() {
8583 invalidate();
8584 }
Cary Clarkd6982c92009-05-29 11:02:22 -04008585
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05008586 /**
Leon Scroggins9ab32b62010-05-03 14:19:50 +01008587 * Pass the key directly to the page. This assumes that
8588 * nativePageShouldHandleShiftAndArrows() returned true.
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05008589 */
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07008590 private void letPageHandleNavKey(int keyCode, long time, boolean down, int metaState) {
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05008591 int keyEventAction;
8592 int eventHubAction;
8593 if (down) {
8594 keyEventAction = KeyEvent.ACTION_DOWN;
8595 eventHubAction = EventHub.KEY_DOWN;
8596 playSoundEffect(keyCodeToSoundsEffect(keyCode));
8597 } else {
8598 keyEventAction = KeyEvent.ACTION_UP;
8599 eventHubAction = EventHub.KEY_UP;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008600 }
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07008601
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05008602 KeyEvent event = new KeyEvent(time, time, keyEventAction, keyCode,
Svetoslav Ganov9b756ac2010-11-05 15:42:22 -07008603 1, (metaState & KeyEvent.META_SHIFT_ON)
8604 | (metaState & KeyEvent.META_ALT_ON)
8605 | (metaState & KeyEvent.META_SYM_ON)
Jeff Brown6b53e8d2010-11-10 16:03:06 -08008606 , KeyCharacterMap.VIRTUAL_KEYBOARD, 0, 0);
Leon Scrogginsb127c8f2010-03-05 15:42:17 -05008607 mWebViewCore.sendMessage(eventHubAction, event);
8608 }
8609
8610 // return true if the key was handled
8611 private boolean navHandledKey(int keyCode, int count, boolean noScroll,
8612 long time) {
8613 if (mNativeClass == 0) {
8614 return false;
Cary Clark215b72c2009-06-26 14:38:43 -04008615 }
Cary Clark32820242010-12-03 10:27:20 -05008616 mInitialHitTestResult = null;
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008617 mLastCursorTime = time;
8618 mLastCursorBounds = nativeGetCursorRingBounds();
8619 boolean keyHandled
8620 = nativeMoveCursor(keyCode, count, noScroll) == false;
Derek Sollenberger2e5c1502009-06-03 10:44:42 -04008621 if (DebugFlags.WEB_VIEW) {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008622 Log.v(LOGTAG, "navHandledKey mLastCursorBounds=" + mLastCursorBounds
8623 + " mLastCursorTime=" + mLastCursorTime
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008624 + " handled=" + keyHandled);
8625 }
Leon Scrogginscef1c592011-01-26 11:13:24 -05008626 if (keyHandled == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008627 return keyHandled;
8628 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008629 Rect contentCursorRingBounds = nativeGetCursorRingBounds();
8630 if (contentCursorRingBounds.isEmpty()) return keyHandled;
Mike Reede9e86b82009-09-15 11:26:53 -04008631 Rect viewCursorRingBounds = contentToViewRect(contentCursorRingBounds);
Cary Clarka3ee56f2011-01-10 11:20:56 -05008632 // set last touch so that context menu related functions will work
8633 mLastTouchX = (viewCursorRingBounds.left + viewCursorRingBounds.right) / 2;
8634 mLastTouchY = (viewCursorRingBounds.top + viewCursorRingBounds.bottom) / 2;
Leon Scrogginscef1c592011-01-26 11:13:24 -05008635 if (mHeightCanMeasure == false) {
8636 return keyHandled;
8637 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008638 Rect visRect = new Rect();
8639 calcOurVisibleRect(visRect);
8640 Rect outset = new Rect(visRect);
8641 int maxXScroll = visRect.width() / 2;
8642 int maxYScroll = visRect.height() / 2;
8643 outset.inset(-maxXScroll, -maxYScroll);
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008644 if (Rect.intersects(outset, viewCursorRingBounds) == false) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008645 return keyHandled;
8646 }
8647 // FIXME: Necessary because ScrollView/ListView do not scroll left/right
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008648 int maxH = Math.min(viewCursorRingBounds.right - visRect.right,
8649 maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008650 if (maxH > 0) {
8651 pinScrollBy(maxH, 0, true, 0);
8652 } else {
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008653 maxH = Math.max(viewCursorRingBounds.left - visRect.left,
8654 -maxXScroll);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008655 if (maxH < 0) {
8656 pinScrollBy(maxH, 0, true, 0);
8657 }
8658 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008659 if (mLastCursorBounds.isEmpty()) return keyHandled;
8660 if (mLastCursorBounds.equals(contentCursorRingBounds)) {
8661 return keyHandled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008662 }
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008663 if (DebugFlags.WEB_VIEW) {
8664 Log.v(LOGTAG, "navHandledKey contentCursorRingBounds="
8665 + contentCursorRingBounds);
8666 }
8667 requestRectangleOnScreen(viewCursorRingBounds);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008668 return keyHandled;
8669 }
Cary Clarkd6982c92009-05-29 11:02:22 -04008670
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008671 /**
Svetoslav Ganov12bed782011-01-03 14:14:50 -08008672 * @return Whether accessibility script has been injected.
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07008673 */
Svetoslav Ganov12bed782011-01-03 14:14:50 -08008674 private boolean accessibilityScriptInjected() {
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07008675 // TODO: Maybe the injected script should announce its presence in
8676 // the page meta-tag so the nativePageShouldHandleShiftAndArrows
8677 // will check that as one of the conditions it looks for
Svetoslav Ganov12bed782011-01-03 14:14:50 -08008678 return mAccessibilityScriptInjected;
Svetoslav Ganov585f13f8d2010-08-10 07:59:15 -07008679 }
8680
8681 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008682 * Set the background color. It's white by default. Pass
8683 * zero to make the view transparent.
8684 * @param color the ARGB color described by Color.java
8685 */
Gilles Debunne0e7d652d2011-02-22 15:26:14 -08008686 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008687 public void setBackgroundColor(int color) {
8688 mBackgroundColor = color;
8689 mWebViewCore.sendMessage(EventHub.SET_BACKGROUND_COLOR, color);
8690 }
8691
Kristian Monsen9f5f7af2011-02-24 11:14:11 +00008692 /**
8693 * @deprecated This method is now obsolete.
8694 */
8695 @Deprecated
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008696 public void debugDump() {
8697 nativeDebugDump();
8698 mWebViewCore.sendMessage(EventHub.DUMP_NAVTREE);
8699 }
Cary Clarkd6982c92009-05-29 11:02:22 -04008700
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008701 /**
Mike Reedefe2c722009-10-14 09:42:02 -04008702 * Draw the HTML page into the specified canvas. This call ignores any
8703 * view-specific zoom, scroll offset, or other changes. It does not draw
8704 * any view-specific chrome, such as progress or URL bars.
8705 *
8706 * @hide only needs to be accessible to Browser and testing
8707 */
8708 public void drawPage(Canvas canvas) {
Grace Kloba8abd50b2010-07-08 15:02:14 -07008709 nativeDraw(canvas, 0, 0, false);
Mike Reedefe2c722009-10-14 09:42:02 -04008710 }
8711
8712 /**
Teng-Hui Zhu661e8b12011-03-02 15:09:34 -08008713 * Enable the communication b/t the webView and VideoViewProxy
8714 *
8715 * @hide only used by the Browser
8716 */
8717 public void setHTML5VideoViewProxy(HTML5VideoViewProxy proxy) {
8718 mHTML5VideoViewProxy = proxy;
8719 }
8720
8721 /**
Dave Burke84a34052011-02-01 12:57:54 +00008722 * Enable expanded tiles bound for smoother scrolling.
8723 *
8724 * @hide only used by the Browser
8725 */
8726 public void setExpandedTileBounds(boolean enabled) {
Ben Murdochfa148f62011-02-01 20:51:27 +00008727 nativeSetExpandedTileBounds(enabled);
Dave Burke84a34052011-02-01 12:57:54 +00008728 }
8729
8730 /**
Ben Murdochecbc65c2010-01-13 10:54:56 +00008731 * Set the time to wait between passing touches to WebCore. See also the
8732 * TOUCH_SENT_INTERVAL member for further discussion.
8733 *
8734 * @hide This is only used by the DRT test application.
8735 */
8736 public void setTouchInterval(int interval) {
8737 mCurrentTouchInterval = interval;
8738 }
8739
8740 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008741 * Update our cache with updatedText.
8742 * @param updatedText The new text to put in our cache.
8743 */
8744 /* package */ void updateCachedTextfield(String updatedText) {
8745 // Also place our generation number so that when we look at the cache
8746 // we recognize that it is up to date.
8747 nativeUpdateCachedTextfield(updatedText, mTextGeneration);
8748 }
Cary Clarkd6982c92009-05-29 11:02:22 -04008749
Ben Murdoch62275a42010-09-07 11:27:28 +01008750 /*package*/ void autoFillForm(int autoFillQueryId) {
8751 mWebViewCore.sendMessage(EventHub.AUTOFILL_FORM, autoFillQueryId, /* unused */0);
8752 }
8753
Ben Murdoch1708ad52010-11-11 15:56:16 +00008754 /* package */ ViewManager getViewManager() {
8755 return mViewManager;
8756 }
8757
Cary Clark1cb97ee2009-12-11 12:10:36 -05008758 private native int nativeCacheHitFramePointer();
Cary Clarkb2601352011-01-11 11:32:01 -05008759 private native boolean nativeCacheHitIsPlugin();
Cary Clark1cb97ee2009-12-11 12:10:36 -05008760 private native Rect nativeCacheHitNodeBounds();
8761 private native int nativeCacheHitNodePointer();
Leon Scroggins4890feb2009-07-02 10:37:10 -04008762 /* package */ native void nativeClearCursor();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008763 private native void nativeCreate(int ptr);
Cary Clarkd6982c92009-05-29 11:02:22 -04008764 private native int nativeCursorFramePointer();
8765 private native Rect nativeCursorNodeBounds();
Cary Clarkaffa5d22010-01-07 12:18:23 -05008766 private native int nativeCursorNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04008767 private native boolean nativeCursorIntersects(Rect visibleRect);
8768 private native boolean nativeCursorIsAnchor();
8769 private native boolean nativeCursorIsTextInput();
Cary Clarked56eda2009-06-18 09:48:47 -04008770 private native Point nativeCursorPosition();
Cary Clarkd6982c92009-05-29 11:02:22 -04008771 private native String nativeCursorText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008772 /**
8773 * Returns true if the native cursor node says it wants to handle key events
8774 * (ala plugins). This can only be called if mNativeClass is non-zero!
8775 */
Cary Clark2f1d60c2009-06-03 08:05:53 -04008776 private native boolean nativeCursorWantsKeyEvents();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008777 private native void nativeDebugDump();
8778 private native void nativeDestroy();
Grace Kloba8abd50b2010-07-08 15:02:14 -07008779
8780 /**
8781 * Draw the picture set with a background color and extra. If
8782 * "splitIfNeeded" is true and the return value is not 0, the return value
8783 * MUST be passed to WebViewCore with SPLIT_PICTURE_SET message so that the
8784 * native allocation can be freed.
8785 */
8786 private native int nativeDraw(Canvas canvas, int color, int extra,
8787 boolean splitIfNeeded);
Cary Clarkd6982c92009-05-29 11:02:22 -04008788 private native void nativeDumpDisplayTree(String urlOrNull);
Cary Clark924af702010-06-04 16:37:43 -04008789 private native boolean nativeEvaluateLayersAnimations();
Romain Guycabfcc12011-03-07 18:06:46 -08008790 private native int nativeGetDrawGLFunction(Rect rect, Rect viewRect,
8791 float scale, int extras);
8792 private native void nativeUpdateDrawGLFunction(Rect rect, Rect viewRect);
Cary Clark924af702010-06-04 16:37:43 -04008793 private native void nativeExtendSelection(int x, int y);
Leon Scroggins4f4a5672010-10-19 13:58:11 -04008794 private native int nativeFindAll(String findLower, String findUpper,
8795 boolean sameAsLastSearch);
Cary Clarkd6982c92009-05-29 11:02:22 -04008796 private native void nativeFindNext(boolean forward);
Leon Scroggins3a503392010-01-06 17:04:38 -05008797 /* package */ native int nativeFocusCandidateFramePointer();
Leon Scrogginsf9b1a092010-03-31 15:32:36 -04008798 /* package */ native boolean nativeFocusCandidateHasNextTextfield();
Leon Scroggins69ec5c22010-04-22 16:27:21 -04008799 /* package */ native boolean nativeFocusCandidateIsPassword();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008800 private native boolean nativeFocusCandidateIsRtlText();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008801 private native boolean nativeFocusCandidateIsTextInput();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05008802 /* package */ native int nativeFocusCandidateMaxLength();
Leon Scrogginsae0238c2011-01-05 15:12:55 -05008803 /* package */ native boolean nativeFocusCandidateIsAutoComplete();
Leon Scroggins0ca70882009-06-26 17:45:29 -04008804 /* package */ native String nativeFocusCandidateName();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008805 private native Rect nativeFocusCandidateNodeBounds();
Leon Scrogginsdfc07272010-10-12 13:40:23 -04008806 /**
8807 * @return A Rect with left, top, right, bottom set to the corresponding
8808 * padding values in the focus candidate, if it is a textfield/textarea with
8809 * a style. Otherwise return null. This is not actually a rectangle; Rect
8810 * is being used to pass four integers.
8811 */
8812 private native Rect nativeFocusCandidatePaddingRect();
Leon Scroggins69ec5c22010-04-22 16:27:21 -04008813 /* package */ native int nativeFocusCandidatePointer();
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008814 private native String nativeFocusCandidateText();
Leon Scroggins56a102c2010-11-12 14:40:07 -05008815 /* package */ native float nativeFocusCandidateTextSize();
Leon Scroggins1ca56262010-11-18 14:03:03 -05008816 /* package */ native int nativeFocusCandidateLineHeight();
Leon Scrogginsaa7b9d72009-12-07 13:38:07 -05008817 /**
8818 * Returns an integer corresponding to WebView.cpp::type.
8819 * See WebTextView.setType()
8820 */
8821 private native int nativeFocusCandidateType();
Derek Sollenberger718d69f2009-10-19 15:56:43 -04008822 private native boolean nativeFocusIsPlugin();
Cary Clarke84a0db2010-03-17 15:58:20 -04008823 private native Rect nativeFocusNodeBounds();
Leon Scroggins4890feb2009-07-02 10:37:10 -04008824 /* package */ native int nativeFocusNodePointer();
Cary Clarkd6982c92009-05-29 11:02:22 -04008825 private native Rect nativeGetCursorRingBounds();
Cary Clark57d2c3a2009-12-23 13:57:15 -05008826 private native String nativeGetSelection();
Cary Clarkd6982c92009-05-29 11:02:22 -04008827 private native boolean nativeHasCursorNode();
8828 private native boolean nativeHasFocusNode();
Cary Clarke872f3a2009-06-11 09:51:11 -04008829 private native void nativeHideCursor();
Cary Clark924af702010-06-04 16:37:43 -04008830 private native boolean nativeHitSelection(int x, int y);
Cary Clarkd6982c92009-05-29 11:02:22 -04008831 private native String nativeImageURI(int x, int y);
8832 private native void nativeInstrumentReport();
Cary Clark6f5dfc62010-11-11 13:09:20 -05008833 private native Rect nativeLayerBounds(int layer);
Leon Scroggins1be40982010-03-01 11:20:31 -05008834 /* package */ native boolean nativeMoveCursorToNextTextInput();
Cary Clarkd6982c92009-05-29 11:02:22 -04008835 // return true if the page has been scrolled
8836 private native boolean nativeMotionUp(int x, int y, int slop);
8837 // returns false if it handled the key
Leon Scroggins1c7f8c52009-06-05 13:49:26 -04008838 private native boolean nativeMoveCursor(int keyCode, int count,
Cary Clarkd6982c92009-05-29 11:02:22 -04008839 boolean noScroll);
8840 private native int nativeMoveGeneration();
Cary Clark924af702010-06-04 16:37:43 -04008841 private native void nativeMoveSelection(int x, int y);
Leon Scroggins9ab32b62010-05-03 14:19:50 +01008842 /**
8843 * @return true if the page should get the shift and arrow keys, rather
8844 * than select text/navigation.
8845 *
8846 * If the focus is a plugin, or if the focus and cursor match and are
8847 * a contentEditable element, then the page should handle these keys.
8848 */
8849 private native boolean nativePageShouldHandleShiftAndArrows();
Cary Clark1cb97ee2009-12-11 12:10:36 -05008850 private native boolean nativePointInNavCache(int x, int y, int slop);
Cary Clarkd6982c92009-05-29 11:02:22 -04008851 // Like many other of our native methods, you must make sure that
8852 // mNativeClass is not null before calling this method.
8853 private native void nativeRecordButtons(boolean focused,
8854 boolean pressed, boolean invalidate);
Cary Clark924af702010-06-04 16:37:43 -04008855 private native void nativeResetSelection();
Cary Clark2cdee232010-12-29 14:59:24 -05008856 private native Point nativeSelectableText();
Cary Clark924af702010-06-04 16:37:43 -04008857 private native void nativeSelectAll();
Cary Clarkd6982c92009-05-29 11:02:22 -04008858 private native void nativeSelectBestAt(Rect rect);
Svetoslav Ganov9504f572011-01-14 11:38:17 -08008859 private native void nativeSelectAt(int x, int y);
Cary Clark924af702010-06-04 16:37:43 -04008860 private native int nativeSelectionX();
8861 private native int nativeSelectionY();
Leon Scroggins6a367f52010-05-06 17:37:40 -04008862 private native int nativeFindIndex();
Cary Clark924af702010-06-04 16:37:43 -04008863 private native void nativeSetExtendSelection();
Cary Clarkde023c12010-03-03 10:05:16 -05008864 private native void nativeSetFindIsEmpty();
8865 private native void nativeSetFindIsUp(boolean isUp);
Cary Clarkd6982c92009-05-29 11:02:22 -04008866 private native void nativeSetHeightCanMeasure(boolean measure);
Nicolas Roard3cb5ded2011-02-23 18:18:46 -08008867 private native void nativeSetBaseLayer(int layer, Region invalRegion,
Shimeng (Simon) Wang464b6902011-03-16 11:27:44 -07008868 boolean showVisualIndicator, boolean isPictureAfterFirstLayout);
Cary Clarke60cb7f2010-08-25 14:56:00 -04008869 private native void nativeShowCursorTimed();
Grace Kloba8abd50b2010-07-08 15:02:14 -07008870 private native void nativeReplaceBaseContent(int content);
8871 private native void nativeCopyBaseContentToPicture(Picture pict);
8872 private native boolean nativeHasContent();
Cary Clark2ec30692010-02-23 10:50:38 -05008873 private native void nativeSetSelectionPointer(boolean set,
Cary Clark924af702010-06-04 16:37:43 -04008874 float scale, int x, int y);
8875 private native boolean nativeStartSelection(int x, int y);
Cary Clarkd9fd8572011-02-03 05:25:05 -05008876 private native void nativeStopGL();
Cary Clark31b83672010-03-09 09:20:34 -05008877 private native Rect nativeSubtractLayers(Rect content);
Cary Clarkd6982c92009-05-29 11:02:22 -04008878 private native int nativeTextGeneration();
8879 // Never call this version except by updateCachedTextfield(String) -
8880 // we always want to pass in our generation number.
8881 private native void nativeUpdateCachedTextfield(String updatedText,
8882 int generation);
Cary Clark924af702010-06-04 16:37:43 -04008883 private native boolean nativeWordSelection(int x, int y);
Grace Kloba8b97e4b2009-07-28 13:11:38 -07008884 // return NO_LEFTEDGE means failure.
Derek Sollenberger15c5ddb2010-06-10 12:31:29 -04008885 static final int NO_LEFTEDGE = -1;
8886 native int nativeGetBlockLeftEdge(int x, int y, float scale);
Patrick Scotta3ebcc92010-07-16 11:52:22 -04008887
Ben Murdochfa148f62011-02-01 20:51:27 +00008888 private native void nativeSetExpandedTileBounds(boolean enabled);
8889
Patrick Scotta3ebcc92010-07-16 11:52:22 -04008890 // Returns a pointer to the scrollable LayerAndroid at the given point.
Cary Clarkb9aaa772011-01-07 16:14:54 -05008891 private native int nativeScrollableLayer(int x, int y, Rect scrollRect,
8892 Rect scrollBounds);
Leon Scrogginsd5188652011-01-19 17:01:55 -05008893 /**
8894 * Scroll the specified layer.
8895 * @param layer Id of the layer to scroll, as determined by nativeScrollableLayer.
8896 * @param newX Destination x position to which to scroll.
8897 * @param newY Destination y position to which to scroll.
8898 * @return True if the layer is successfully scrolled.
8899 */
8900 private native boolean nativeScrollLayer(int layer, int newX, int newY);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08008901}