Merge change 8437

* changes:
  Re-add double tap to the Browser. It only works if WebView uses wide viewport. It is also controlled by ENABLE_DOUBLETAP_ZOOM flag. Currently the default is on.
This commit is contained in:
Android (Google) Code Review
2009-07-28 13:45:58 -07:00
2 changed files with 203 additions and 25 deletions

View File

@@ -351,6 +351,9 @@ public class WebView extends AbsoluteLayout
VelocityTracker mVelocityTracker;
private int mMaximumFling;
// use this flag to control whether enabling the new double tap zoom
static final boolean ENABLE_DOUBLETAP_ZOOM = true;
/**
* Touch mode
*/
@@ -370,6 +373,7 @@ public class WebView extends AbsoluteLayout
private static final int SCROLL_ZOOM_OUT = 11;
private static final int LAST_SCROLL_ZOOM = 11;
// end of touch mode values specific to scale+scroll
private static final int TOUCH_DOUBLE_TAP_MODE = 12;
// Whether to forward the touch events to WebCore
private boolean mForwardTouchEvents = false;
@@ -394,6 +398,8 @@ public class WebView extends AbsoluteLayout
*/
// pre-computed square of ViewConfiguration.getScaledTouchSlop()
private int mTouchSlopSquare;
// pre-computed square of ViewConfiguration.getScaledDoubleTapSlop()
private int mDoubleTapSlopSquare;
// pre-computed density adjusted navigation slop
private int mNavSlop;
// This should be ViewConfiguration.getTapTimeout()
@@ -450,6 +456,7 @@ public class WebView extends AbsoluteLayout
private static final int NEVER_REMEMBER_PASSWORD = 2;
private static final int SWITCH_TO_SHORTPRESS = 3;
private static final int SWITCH_TO_LONGPRESS = 4;
private static final int RELEASE_SINGLE_TAP = 5;
private static final int REQUEST_FORM_DATA = 6;
private static final int SWITCH_TO_CLICK = 7;
private static final int RESUME_WEBCORE_UPDATE = 8;
@@ -482,7 +489,7 @@ public class WebView extends AbsoluteLayout
"NEVER_REMEMBER_PASSWORD", // = 2;
"SWITCH_TO_SHORTPRESS", // = 3;
"SWITCH_TO_LONGPRESS", // = 4;
"5",
"RELEASE_SINGLE_TAP", // = 5;
"REQUEST_FORM_DATA", // = 6;
"SWITCH_TO_CLICK", // = 7;
"RESUME_WEBCORE_UPDATE", // = 8;
@@ -521,6 +528,16 @@ public class WebView extends AbsoluteLayout
// initial scale in percent. 0 means using default.
private int mInitialScale = 0;
// while in the zoom overview mode, the page's width is fully fit to the
// current window. The page is alive, in another words, you can click to
// follow the links. Double tap will toggle between zoom overview mode and
// the last zoom scale.
boolean mInZoomOverview = false;
// ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
// engadget always have wider mContentWidth no matter what viewport size is.
int mZoomOverviewWidth = 0;
float mLastScale;
// default scale. Depending on the display density.
static int DEFAULT_SCALE_PERCENT;
private float mDefaultScale;
@@ -762,9 +779,11 @@ public class WebView extends AbsoluteLayout
setLongClickable(true);
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
final int slop = configuration.getScaledTouchSlop();
int slop = configuration.getScaledTouchSlop();
mTouchSlopSquare = slop * slop;
mMinLockSnapReverseDistance = slop;
slop = configuration.getScaledDoubleTapSlop();
mDoubleTapSlopSquare = slop * slop;
final float density = getContext().getResources().getDisplayMetrics().density;
// use one line height, 16 based on our current default font, for how
// far we allow a touch be away from the edge of a link
@@ -1124,6 +1143,9 @@ public class WebView extends AbsoluteLayout
b.putInt("scrollX", mScrollX);
b.putInt("scrollY", mScrollY);
b.putFloat("scale", mActualScale);
if (mInZoomOverview) {
b.putFloat("lastScale", mLastScale);
}
return true;
}
return false;
@@ -1168,6 +1190,13 @@ public class WebView extends AbsoluteLayout
// onSizeChanged() is called, the rest will be set
// correctly
mActualScale = scale;
float lastScale = b.getFloat("lastScale", -1.0f);
if (lastScale > 0) {
mInZoomOverview = true;
mLastScale = lastScale;
} else {
mInZoomOverview = false;
}
invalidate();
return true;
}
@@ -3060,6 +3089,11 @@ public class WebView extends AbsoluteLayout
float y = mLastTouchY + (float) (mScrollY - lp.y);
mWebTextView.fakeTouchEvent(x, y);
}
if (mInZoomOverview) {
// if in zoom overview mode, call doDoubleTap() to bring it back
// to normal mode so that user can enter text.
doDoubleTap();
}
}
else { // used by plugins
imm.showSoftInput(this, 0);
@@ -3634,7 +3668,9 @@ public class WebView extends AbsoluteLayout
// update mMinZoomScale if the minimum zoom scale is not fixed
if (!mMinZoomScaleFixed) {
mMinZoomScale = (float) getViewWidth()
/ Math.max(ZOOM_OUT_WIDTH, mContentWidth);
/ Math.max(ZOOM_OUT_WIDTH, mDrawHistory ? mHistoryPicture
.getWidth() : (mZoomOverviewWidth > 0 ?
mZoomOverviewWidth : mContentWidth));
}
// we always force, in case our height changed, in which case we still
@@ -3755,6 +3791,15 @@ public class WebView extends AbsoluteLayout
nativeMoveSelection(viewToContent(mSelectX)
, viewToContent(mSelectY), false);
mTouchSelection = mExtendSelection = true;
} else if (mPrivateHandler.hasMessages(RELEASE_SINGLE_TAP)) {
mPrivateHandler.removeMessages(RELEASE_SINGLE_TAP);
if (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare) {
mTouchMode = TOUCH_DOUBLE_TAP_MODE;
} else {
// commit the short press action for the previous tap
doShortPress();
// continue, mTouchMode should be still TOUCH_INIT_MODE
}
} else {
mTouchMode = TOUCH_INIT_MODE;
mPreventDrag = mForwardTouchEvents;
@@ -3764,7 +3809,8 @@ public class WebView extends AbsoluteLayout
}
}
// Trigger the link
if (mTouchMode == TOUCH_INIT_MODE) {
if (mTouchMode == TOUCH_INIT_MODE
|| mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
mPrivateHandler.sendMessageDelayed(mPrivateHandler
.obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
}
@@ -3810,7 +3856,8 @@ public class WebView extends AbsoluteLayout
if (mTouchMode == TOUCH_SHORTPRESS_MODE
|| mTouchMode == TOUCH_SHORTPRESS_START_MODE) {
mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
} else if (mTouchMode == TOUCH_INIT_MODE) {
} else if (mTouchMode == TOUCH_INIT_MODE
|| mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
}
@@ -3836,7 +3883,7 @@ public class WebView extends AbsoluteLayout
.sendMessage(EventHub.SET_SNAP_ANCHOR, 0, 0);
}
WebSettings settings = getSettings();
if (settings.supportZoom()
if (settings.supportZoom() && !mInZoomOverview
&& settings.getBuiltInZoomControls()
&& !mZoomButtonsController.isVisible()
&& (canZoomScrollOut() ||
@@ -3905,7 +3952,7 @@ public class WebView extends AbsoluteLayout
mUserScroll = true;
}
if (!getSettings().getBuiltInZoomControls()) {
if (!getSettings().getBuiltInZoomControls() && !mInZoomOverview) {
boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
boolean showMagnify = canZoomScrollOut();
if (mZoomControls != null && (showPlusMinus || showMagnify)) {
@@ -3929,7 +3976,22 @@ public class WebView extends AbsoluteLayout
case MotionEvent.ACTION_UP: {
mLastTouchUpTime = eventTime;
switch (mTouchMode) {
case TOUCH_DOUBLE_TAP_MODE: // double tap
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
doDoubleTap();
break;
case TOUCH_INIT_MODE: // tap
if (ENABLE_DOUBLETAP_ZOOM) {
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
if (!mPreventDrag) {
mPrivateHandler.sendMessageDelayed(
mPrivateHandler.obtainMessage(
RELEASE_SINGLE_TAP),
ViewConfiguration.getDoubleTapTimeout());
}
break;
}
// fall through
case TOUCH_SHORTPRESS_START_MODE:
case TOUCH_SHORTPRESS_MODE:
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
@@ -4365,6 +4427,9 @@ public class WebView extends AbsoluteLayout
mInvInitialZoomScale = 1.0f / oldScale;
mInvFinalZoomScale = 1.0f / mActualScale;
mZoomScale = mActualScale;
if (!mInZoomOverview) {
mLastScale = scale;
}
invalidate();
return true;
} else {
@@ -4470,6 +4535,9 @@ public class WebView extends AbsoluteLayout
public boolean zoomIn() {
// TODO: alternatively we can disallow this during draw history mode
switchOutDrawHistory();
// Center zooming to the center of the screen.
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
return zoomWithPreview(mActualScale * 1.25f);
}
@@ -4480,6 +4548,9 @@ public class WebView extends AbsoluteLayout
public boolean zoomOut() {
// TODO: alternatively we can disallow this during draw history mode
switchOutDrawHistory();
// Center zooming to the center of the screen.
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
return zoomWithPreview(mActualScale * 0.8f);
}
@@ -4571,6 +4642,50 @@ public class WebView extends AbsoluteLayout
}
}
private void doDoubleTap() {
if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
return;
}
mZoomCenterX = mLastTouchX;
mZoomCenterY = mLastTouchY;
mInZoomOverview = !mInZoomOverview;
if (mInZoomOverview) {
float newScale = (float) getViewWidth()
/ (mZoomOverviewWidth > 0 ? mZoomOverviewWidth
: mContentWidth);
if (Math.abs(newScale - mActualScale) < 0.01) {
mInZoomOverview = !mInZoomOverview;
// as it is already full screen, do nothing.
return;
}
if (getSettings().getBuiltInZoomControls()) {
if (mZoomButtonsController.isVisible()) {
mZoomButtonsController.setVisible(false);
}
} else {
if (mZoomControlRunnable != null) {
mPrivateHandler.removeCallbacks(mZoomControlRunnable);
}
if (mZoomControls != null) {
mZoomControls.hide();
}
}
zoomWithPreview(newScale);
} else {
// mLastTouchX and mLastTouchY are the point in the current viewport
int contentX = viewToContent((int) mLastTouchX + mScrollX);
int contentY = viewToContent((int) mLastTouchY + mScrollY);
int left = nativeGetBlockLeftEdge(contentX, contentY);
if (left != NO_LEFTEDGE) {
// add a 5pt padding to the left edge. Re-calculate the zoom
// center so that the new scroll x will be on the left edge.
mZoomCenterX = left < 5 ? 0 : (left - 5) * mLastScale
* mActualScale / (mLastScale - mActualScale);
}
zoomWithPreview(mLastScale);
}
}
// Called by JNI to handle a touch on a node representing an email address,
// address, or phone number
private void overrideLoading(String url) {
@@ -4791,6 +4906,8 @@ public class WebView extends AbsoluteLayout
if (mTouchMode == TOUCH_INIT_MODE) {
mTouchMode = TOUCH_SHORTPRESS_START_MODE;
updateSelection();
} else if (mTouchMode == TOUCH_DOUBLE_TAP_MODE) {
mTouchMode = TOUCH_DONE_MODE;
}
break;
}
@@ -4802,6 +4919,13 @@ public class WebView extends AbsoluteLayout
}
break;
}
case RELEASE_SINGLE_TAP: {
if (!mPreventDrag) {
mTouchMode = TOUCH_DONE_MODE;
doShortPress();
}
break;
}
case SWITCH_TO_CLICK:
// The user clicked with the trackball, and did not click a
// second time, so perform the action of a trackball single
@@ -4854,6 +4978,7 @@ public class WebView extends AbsoluteLayout
break;
case NEW_PICTURE_MSG_ID:
// called for new content
final int viewWidth = getViewWidth();
final WebViewCore.DrawData draw =
(WebViewCore.DrawData) msg.obj;
final Point viewSize = draw.mViewPoint;
@@ -4861,16 +4986,12 @@ public class WebView extends AbsoluteLayout
// use the same logic in sendViewSizeZoom() to make sure
// the mZoomScale has matched the viewSize so that we
// can clear mZoomScale
if (Math.round(getViewWidth() / mZoomScale) == viewSize.x) {
if (Math.round(viewWidth / mZoomScale) == viewSize.x) {
mZoomScale = 0;
mWebViewCore.sendMessage(EventHub.SET_SNAP_ANCHOR,
0, 0);
}
}
if (!mMinZoomScaleFixed) {
mMinZoomScale = (float) getViewWidth()
/ Math.max(ZOOM_OUT_WIDTH, draw.mWidthHeight.x);
}
// We update the layout (i.e. request a layout from the
// view system) if the last view size that we sent to
// WebCore matches the view size of the picture we just
@@ -4888,6 +5009,25 @@ public class WebView extends AbsoluteLayout
if (mPictureListener != null) {
mPictureListener.onNewPicture(WebView.this, capturePicture());
}
if (mWebViewCore.getSettings().getUseWideViewPort()) {
mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
draw.mViewPoint.x);
}
if (!mMinZoomScaleFixed) {
mMinZoomScale = (float) viewWidth
/ Math.max(ZOOM_OUT_WIDTH,
mZoomOverviewWidth > 0 ? mZoomOverviewWidth
: mContentWidth);
}
if (!mDrawHistory && mInZoomOverview) {
// fit the content width to the current view. Ignore
// the rounding error case.
if (Math.abs((viewWidth * mInvActualScale)
- mZoomOverviewWidth) > 1) {
zoomWithPreview((float) viewWidth
/ mZoomOverviewWidth);
}
}
break;
case WEBCORE_INITIALIZED_MSG_ID:
// nativeCreate sets mNativeClass to a non-zero value
@@ -4916,7 +5056,7 @@ public class WebView extends AbsoluteLayout
}
}
break;
case DID_FIRST_LAYOUT_MSG_ID:
case DID_FIRST_LAYOUT_MSG_ID: {
if (mNativeClass == 0) {
break;
}
@@ -4943,15 +5083,17 @@ public class WebView extends AbsoluteLayout
if (width == 0) {
break;
}
final WebSettings settings = mWebViewCore.getSettings();
int initialScale = msg.arg1;
int viewportWidth = msg.arg2;
// start a new page with DEFAULT_SCALE zoom scale.
float scale = mDefaultScale;
mInZoomOverview = false;
if (mInitialScale > 0) {
scale = mInitialScale / 100.0f;
} else {
if (initialScale < 0) break;
if (mWebViewCore.getSettings().getUseWideViewPort()) {
if (initialScale == -1) break;
if (settings.getUseWideViewPort()) {
// force viewSizeChanged by setting mLastWidthSent
// to 0
mLastWidthSent = 0;
@@ -4961,11 +5103,21 @@ public class WebView extends AbsoluteLayout
// than the view width, zoom in to fill the view
if (viewportWidth > 0 && viewportWidth < width) {
scale = (float) width / viewportWidth;
} else {
if (settings.getUseWideViewPort()) {
mInZoomOverview = ENABLE_DOUBLETAP_ZOOM;
}
}
} else if (initialScale < 0) {
// this should only happen when
// ENABLE_DOUBLETAP_ZOOM is true
mInZoomOverview = true;
scale = -initialScale / 100.0f;
} else {
scale = initialScale / 100.0f;
}
}
mLastScale = scale;
setNewZoomScale(scale, false);
// As we are on a new page, remove the WebTextView. This
// is necessary for page loads driven by webkit, and in
@@ -4973,6 +5125,7 @@ public class WebView extends AbsoluteLayout
// the WebTextView was visible.
clearTextEntry();
break;
}
case MOVE_OUT_OF_PLUGIN:
if (nativePluginEatsNavKey()) {
navHandledKey(msg.arg1, 1, false, 0, true);
@@ -5542,4 +5695,7 @@ public class WebView extends AbsoluteLayout
private native void nativeUpdateCachedTextfield(String updatedText,
int generation);
private native void nativeUpdatePluginReceivesEvents();
// return NO_LEFTEDGE means failure.
private static final int NO_LEFTEDGE = -1;
private native int nativeGetBlockLeftEdge(int x, int y);
}

View File

@@ -96,7 +96,8 @@ final class WebViewCore {
private boolean mViewportUserScalable = true;
private int mRestoredScale = WebView.DEFAULT_SCALE_PERCENT;
private int mRestoredScale = 0;
private int mRestoredScreenWidthScale = 0;
private int mRestoredX = 0;
private int mRestoredY = 0;
@@ -1340,9 +1341,8 @@ final class WebViewCore {
Log.w(LOGTAG, "skip viewSizeChanged as w is 0");
return;
}
if (mSettings.getUseWideViewPort()
&& (w < mViewportWidth || mViewportWidth == -1)) {
int width = mViewportWidth;
if (mSettings.getUseWideViewPort()) {
int width;
if (mViewportWidth == -1) {
if (mSettings.getLayoutAlgorithm() ==
WebSettings.LayoutAlgorithm.NORMAL) {
@@ -1362,9 +1362,15 @@ final class WebViewCore {
*/
width = Math.max(w, nativeGetContentMinPrefWidth());
}
} else {
width = Math.max(w, mViewportWidth);
}
nativeSetSize(width, Math.round((float) width * h / w), w, scale,
w, h);
// while in zoom overview mode, the text are wrapped to the screen
// width matching mWebView.mLastScale. So that we don't trigger
// re-flow while toggling between overview mode and normal mode.
nativeSetSize(width, Math.round((float) width * h / w),
Math.round(mWebView.mInZoomOverview ? w * scale
/ mWebView.mLastScale : w), scale, w, h);
} else {
nativeSetSize(w, h, w, scale, w, h);
}
@@ -1409,6 +1415,7 @@ final class WebViewCore {
public Region mInvalRegion;
public Point mViewPoint;
public Point mWidthHeight;
public int mMinPrefWidth;
}
private void webkitDraw() {
@@ -1424,6 +1431,9 @@ final class WebViewCore {
// Send the native view size that was used during the most recent
// layout.
draw.mViewPoint = new Point(mCurrentViewWidth, mCurrentViewHeight);
if (WebView.ENABLE_DOUBLETAP_ZOOM && mSettings.getUseWideViewPort()) {
draw.mMinPrefWidth = nativeGetContentMinPrefWidth();
}
if (DebugFlags.WEB_VIEW_CORE) Log.v(LOGTAG, "webkitDraw NEW_PICTURE_MSG_ID");
Message.obtain(mWebView.mPrivateHandler,
WebView.NEW_PICTURE_MSG_ID, draw).sendToTarget();
@@ -1734,9 +1744,10 @@ final class WebViewCore {
if (mRestoredScale > 0) {
Message.obtain(mWebView.mPrivateHandler,
WebView.DID_FIRST_LAYOUT_MSG_ID, mRestoredScale, 0,
WebView.DID_FIRST_LAYOUT_MSG_ID,
mRestoredScreenWidthScale > 0 ?
-mRestoredScreenWidthScale : mRestoredScale, 0,
scaleLimit).sendToTarget();
mRestoredScale = 0;
} else {
// if standardLoad is true, use mViewportInitialScale, otherwise
// pass -1 to the WebView to indicate no change of the scale.
@@ -1764,8 +1775,8 @@ final class WebViewCore {
}
}
// reset restored offset
mRestoredX = mRestoredY = 0;
// reset restored offset, scale
mRestoredX = mRestoredY = mRestoredScale = mRestoredScreenWidthScale = 0;
}
}
@@ -1776,6 +1787,17 @@ final class WebViewCore {
}
}
// called by JNI
private void restoreScreenWidthScale(int scale) {
if (!WebView.ENABLE_DOUBLETAP_ZOOM || !mSettings.getUseWideViewPort()) {
return;
}
if (mBrowserFrame.firstLayoutDone() == false) {
mRestoredScreenWidthScale = scale;
}
}
// called by JNI
private void needTouchEvents(boolean need) {
if (mWebView != null) {