am a2b0d38f: First draft of multitouch in the WebView.

Merge commit 'a2b0d38f8c6311e712c32318d4ebb68e5a2fecef' into eclair-plus-aosp

* commit 'a2b0d38f8c6311e712c32318d4ebb68e5a2fecef':
  First draft of multitouch in the WebView.
This commit is contained in:
Grace Kloba
2010-01-11 10:47:21 -08:00
committed by Android Git Automerger
2 changed files with 401 additions and 172 deletions

View File

@@ -21,6 +21,7 @@ import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.DialogInterface.OnCancelListener;
import android.content.pm.PackageManager;
import android.database.DataSetObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -349,6 +350,7 @@ public class WebView extends AbsoluteLayout
private static final int TOUCH_DOUBLE_TAP_MODE = 6;
private static final int TOUCH_DONE_MODE = 7;
private static final int TOUCH_SELECT_MODE = 8;
private static final int TOUCH_PINCH_DRAG = 9;
// Whether to forward the touch events to WebCore
private boolean mForwardTouchEvents = false;
@@ -460,6 +462,7 @@ public class WebView extends AbsoluteLayout
// obj=Rect in doc coordinates
static final int INVAL_RECT_MSG_ID = 26;
static final int REQUEST_KEYBOARD = 27;
static final int SHOW_RECT_MSG_ID = 28;
static final String[] HandlerDebugString = {
"REMEMBER_PASSWORD", // = 1;
@@ -488,7 +491,8 @@ public class WebView extends AbsoluteLayout
"PREVENT_TOUCH_ID", // = 24;
"WEBCORE_NEED_TOUCH_EVENTS", // = 25;
"INVAL_RECT_MSG_ID", // = 26;
"REQUEST_KEYBOARD" // = 27;
"REQUEST_KEYBOARD", // = 27;
"SHOW_RECT_MSG_ID" // = 28;
};
// default scale limit. Depending on the display density
@@ -511,7 +515,7 @@ public class WebView extends AbsoluteLayout
// ideally mZoomOverviewWidth should be mContentWidth. But sites like espn,
// engadget always have wider mContentWidth no matter what viewport size is.
int mZoomOverviewWidth = WebViewCore.DEFAULT_VIEWPORT_WIDTH;
float mLastScale;
float mTextWrapScale;
// default scale. Depending on the display density.
static int DEFAULT_SCALE_PERCENT;
@@ -743,6 +747,9 @@ public class WebView extends AbsoluteLayout
params;
frameParams.gravity = Gravity.RIGHT;
}
mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH);
}
private void updateZoomButtonsEnabled() {
@@ -785,6 +792,7 @@ public class WebView extends AbsoluteLayout
mDefaultScale = density;
mActualScale = density;
mInvActualScale = 1 / density;
mTextWrapScale = density;
DEFAULT_MAX_ZOOM_SCALE = 4.0f * density;
DEFAULT_MIN_ZOOM_SCALE = 0.25f * density;
mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
@@ -805,7 +813,7 @@ public class WebView extends AbsoluteLayout
mDefaultScale = density;
mMaxZoomScale *= scaleFactor;
mMinZoomScale *= scaleFactor;
setNewZoomScale(mActualScale * scaleFactor, false);
setNewZoomScale(mActualScale * scaleFactor, true, false);
}
}
@@ -1162,9 +1170,8 @@ public class WebView extends AbsoluteLayout
b.putInt("scrollX", mScrollX);
b.putInt("scrollY", mScrollY);
b.putFloat("scale", mActualScale);
if (mInZoomOverview) {
b.putFloat("lastScale", mLastScale);
}
b.putFloat("textwrapScale", mTextWrapScale);
b.putBoolean("overview", mInZoomOverview);
return true;
}
return false;
@@ -1209,13 +1216,8 @@ 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;
}
mTextWrapScale = b.getFloat("textwrapScale", scale);
mInZoomOverview = b.getBoolean("overview");
invalidate();
return true;
}
@@ -1938,12 +1940,18 @@ public class WebView extends AbsoluteLayout
contentSizeChanged(updateLayout);
}
private void setNewZoomScale(float scale, boolean force) {
private void setNewZoomScale(float scale, boolean updateTextWrapScale,
boolean force) {
if (scale < mMinZoomScale) {
scale = mMinZoomScale;
} else if (scale > mMaxZoomScale) {
scale = mMaxZoomScale;
}
if (updateTextWrapScale) {
mTextWrapScale = scale;
// reset mLastHeightSent to force VIEW_SIZE_CHANGED sent to WebKit
mLastHeightSent = 0;
}
if (scale != mActualScale || force) {
if (mDrawHistory) {
// If history Picture is drawn, don't update scroll. They will
@@ -1953,9 +1961,7 @@ public class WebView extends AbsoluteLayout
}
mActualScale = scale;
mInvActualScale = 1 / scale;
if (!mPreviewZoomOnly) {
sendViewSizeZoom();
}
sendViewSizeZoom();
} else {
// update our scroll so we don't appear to jump
// i.e. keep the center of the doc in the center of the view
@@ -1983,10 +1989,9 @@ public class WebView extends AbsoluteLayout
mScrollX = pinLocX(Math.round(sx));
mScrollY = pinLocY(Math.round(sy));
if (!mPreviewZoomOnly) {
sendViewSizeZoom();
sendOurVisibleRect();
}
// update webkit
sendViewSizeZoom();
sendOurVisibleRect();
}
}
}
@@ -1996,6 +2001,8 @@ public class WebView extends AbsoluteLayout
private Rect mLastGlobalRect;
private Rect sendOurVisibleRect() {
if (mPreviewZoomOnly) return mLastVisibleRectSent;
Rect rect = new Rect();
calcOurContentVisibleRect(rect);
// Rect.equals() checks for null input.
@@ -2049,6 +2056,8 @@ public class WebView extends AbsoluteLayout
int mWidth;
int mHeight;
int mTextWrapWidth;
int mAnchorX;
int mAnchorY;
float mScale;
boolean mIgnoreHeight;
}
@@ -2060,6 +2069,8 @@ public class WebView extends AbsoluteLayout
* @return true if new values were sent
*/
private boolean sendViewSizeZoom() {
if (mPreviewZoomOnly) return false;
int viewWidth = getViewWidth();
int newWidth = Math.round(viewWidth * mInvActualScale);
int newHeight = Math.round(getViewHeight() * mInvActualScale);
@@ -2079,13 +2090,11 @@ public class WebView extends AbsoluteLayout
ViewSizeData data = new ViewSizeData();
data.mWidth = newWidth;
data.mHeight = newHeight;
// while in zoom overview mode, the text are wrapped to the screen
// width matching mLastScale. So that we don't trigger re-flow while
// toggling between overview mode and normal mode.
data.mTextWrapWidth = mInZoomOverview ? Math.round(viewWidth
/ mLastScale) : newWidth;
data.mTextWrapWidth = Math.round(viewWidth / mTextWrapScale);;
data.mScale = mActualScale;
data.mIgnoreHeight = mZoomScale != 0 && !mHeightCanMeasure;
data.mAnchorX = mAnchorX;
data.mAnchorY = mAnchorY;
mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED, data);
mLastWidthSent = newWidth;
mLastHeightSent = newHeight;
@@ -2838,6 +2847,38 @@ public class WebView extends AbsoluteLayout
*/
private boolean mNeedToAdjustWebTextView;
// if checkVisibility is false, the WebTextView may trigger a move of
// WebView to bring itself into the view.
private void adjustTextView(boolean checkVisibility) {
Rect contentBounds = nativeFocusCandidateNodeBounds();
Rect vBox = contentToViewRect(contentBounds);
Rect visibleRect = new Rect();
calcOurVisibleRect(visibleRect);
if (!checkVisibility || visibleRect.contains(vBox)) {
// As a result of the zoom, the textfield is now on
// screen. Place the WebTextView in its new place,
// accounting for our new scroll/zoom values.
mWebTextView
.setTextSize(
TypedValue.COMPLEX_UNIT_PX,
contentToViewDimension(nativeFocusCandidateTextSize()));
mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
vBox.height());
// If it is a password field, start drawing the
// WebTextView once again.
if (nativeFocusCandidateIsPassword()) {
mWebTextView.setInPassword(true);
}
} else {
// The textfield is now off screen. The user probably
// was not zooming to see the textfield better. Remove
// the WebTextView. If the user types a key, and the
// textfield is still in focus, we will reconstruct
// the WebTextView and scroll it back on screen.
mWebTextView.remove();
}
}
private void drawCoreAndCursorRing(Canvas canvas, int color,
boolean drawCursorRing) {
if (mDrawHistory) {
@@ -2865,32 +2906,7 @@ public class WebView extends AbsoluteLayout
invalidate();
if (mNeedToAdjustWebTextView) {
mNeedToAdjustWebTextView = false;
Rect contentBounds = nativeFocusCandidateNodeBounds();
Rect vBox = contentToViewRect(contentBounds);
Rect visibleRect = new Rect();
calcOurVisibleRect(visibleRect);
if (visibleRect.contains(vBox)) {
// As a result of the zoom, the textfield is now on
// screen. Place the WebTextView in its new place,
// accounting for our new scroll/zoom values.
mWebTextView.setTextSize(TypedValue.COMPLEX_UNIT_PX,
contentToViewDimension(
nativeFocusCandidateTextSize()));
mWebTextView.setRect(vBox.left, vBox.top, vBox.width(),
vBox.height());
// If it is a password field, start drawing the
// WebTextView once again.
if (nativeFocusCandidateIsPassword()) {
mWebTextView.setInPassword(true);
}
} else {
// The textfield is now off screen. The user probably
// was not zooming to see the textfield better. Remove
// the WebTextView. If the user types a key, and the
// textfield is still in focus, we will reconstruct
// the WebTextView and scroll it back on screen.
mWebTextView.remove();
}
adjustTextView(true);
}
}
// calculate the intermediate scroll position. As we need to use
@@ -2924,11 +2940,11 @@ public class WebView extends AbsoluteLayout
canvas.scale(mActualScale, mActualScale);
}
mWebViewCore.drawContentPicture(canvas, color, animateZoom,
animateScroll);
mWebViewCore.drawContentPicture(canvas, color,
(animateZoom || mPreviewZoomOnly), animateScroll);
if (mNativeClass == 0) return;
if (mShiftIsPressed && !animateZoom) {
if (mShiftIsPressed && !(animateZoom || mPreviewZoomOnly)) {
if (mTouchSelection) {
nativeDrawSelectionRegion(canvas);
} else {
@@ -3029,10 +3045,15 @@ public class WebView extends AbsoluteLayout
if (mWebTextView == null) return;
imm.showSoftInput(mWebTextView, 0);
if (mInZoomOverview) {
// if in zoom overview mode, call doDoubleTap() to bring it back
// to normal mode so that user can enter text.
doDoubleTap();
if (mActualScale < mDefaultScale) {
// bring it back to the default scale so that user can enter
// text.
mInZoomOverview = false;
mZoomCenterX = mLastTouchX;
mZoomCenterY = mLastTouchY;
// do not change text wrap scale so that there is no reflow
setNewZoomScale(mDefaultScale, false, false);
adjustTextView(false);
}
}
else { // used by plugins
@@ -3604,6 +3625,8 @@ public class WebView extends AbsoluteLayout
if (mZoomScale == 0) { // unless we're already zooming
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
}
// update mMinZoomScale if the minimum zoom scale is not fixed
@@ -3626,7 +3649,8 @@ public class WebView extends AbsoluteLayout
// we always force, in case our height changed, in which case we still
// want to send the notification over to webkit
setNewZoomScale(mActualScale, true);
// only update the text wrap scale if width changed.
setNewZoomScale(mActualScale, w != ow, true);
}
@Override
@@ -3674,6 +3698,97 @@ public class WebView extends AbsoluteLayout
private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
// MultiTouch handling
private static boolean mSupportMultiTouch;
private double mPinchDistance;
private int mAnchorX;
private int mAnchorY;
private boolean doMultiTouch(MotionEvent ev) {
int action = ev.getAction();
if ((action & 0xff) == MotionEvent.ACTION_POINTER_DOWN) {
// cancel the single touch handling
cancelTouch();
// reset the zoom overview mode so that the page won't auto grow
mInZoomOverview = false;
// If it is in password mode, turn it off so it does not draw
// misplaced.
if (inEditingMode() && nativeFocusCandidateIsPassword()) {
mWebTextView.setInPassword(false);
}
// start multi (2-pointer) touch
mPreviewZoomOnly = true;
float x0 = ev.getX(0);
float y0 = ev.getY(0);
float x1 = ev.getX(1);
float y1 = ev.getY(1);
mLastTouchTime = ev.getEventTime();
mPinchDistance = Math.sqrt((x0 - x1) * (x0 - x1) + (y0 - y1)
* (y0 - y1));
mZoomCenterX = mZoomCenterY = 0;
} else if ((action & 0xff) == MotionEvent.ACTION_POINTER_UP) {
mPreviewZoomOnly = false;
// for testing only, default don't reflow now
boolean reflowNow = !getSettings().getPluginsEnabled();
// force zoom after mPreviewZoomOnly is set to false so that the new
// view size will be passed to the WebKit
if (reflowNow && (mZoomCenterX != 0) && (mZoomCenterY != 0)) {
mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
}
setNewZoomScale(mActualScale, reflowNow, true);
// call invalidate() to draw without zoom filter
invalidate();
// adjust the edit text view if needed
if (inEditingMode()) {
adjustTextView(true);
}
// start a drag, TOUCH_PINCH_DRAG, can't use TOUCH_INIT_MODE as it
// may trigger the unwanted click, can't use TOUCH_DRAG_MODE as it
// may trigger the unwanted fling.
mTouchMode = TOUCH_PINCH_DRAG;
// action indicates which pointer is UP. Use the other one as drag's
// starting position.
int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
>> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
startTouch(ev.getX(id), ev.getY(id), ev.getEventTime());
} else if (action == MotionEvent.ACTION_MOVE) {
float x0 = ev.getX(0);
float y0 = ev.getY(0);
float x1 = ev.getX(1);
float y1 = ev.getY(1);
double distance = Math.sqrt((x0 - x1) * (x0 - x1) + (y0 - y1)
* (y0 - y1));
float scale = (float) (Math.round(distance / mPinchDistance
* mActualScale * 100) / 100.0);
long time = ev.getEventTime();
// add distance/time checking to avoid the minor shift right before
// lifting the fingers after a pause
if (Math.abs(scale - mActualScale) >= 0.01f
&& ((time - mLastTouchTime) < 300 || Math.abs(distance
- mPinchDistance) > (10 * mDefaultScale))) {
// limit the scale change per step
if (scale > mActualScale) {
scale = Math.min(scale, mActualScale * 1.25f);
} else {
scale = Math.max(scale, mActualScale * 0.8f);
}
mZoomCenterX = (x0 + x1) / 2;
mZoomCenterY = (y0 + y1) / 2;
setNewZoomScale(scale, false, false);
invalidate();
mPinchDistance = distance;
mLastTouchTime = time;
}
} else {
Log.w(LOGTAG, action + " should not happen during doMultiTouch");
return false;
}
return true;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (mNativeClass == 0 || !isClickable() || !isLongClickable()) {
@@ -3685,6 +3800,11 @@ public class WebView extends AbsoluteLayout
+ mTouchMode);
}
if (mSupportMultiTouch && getSettings().supportZoom()
&& ev.getPointerCount() > 1) {
return doMultiTouch(ev);
}
int action = ev.getAction();
float x = ev.getX();
float y = ev.getY();
@@ -3745,6 +3865,7 @@ public class WebView extends AbsoluteLayout
// continue, mTouchMode should be still TOUCH_INIT_MODE
}
} else {
mPreviewZoomOnly = false;
mTouchMode = TOUCH_INIT_MODE;
mPreventDrag = mForwardTouchEvents ? PREVENT_DRAG_MAYBE_YES
: PREVENT_DRAG_NO;
@@ -3762,11 +3883,7 @@ public class WebView extends AbsoluteLayout
.obtainMessage(SWITCH_TO_SHORTPRESS), TAP_TIMEOUT);
}
// Remember where the motion event started
mLastTouchX = x;
mLastTouchY = y;
mLastTouchTime = eventTime;
mVelocityTracker = VelocityTracker.obtain();
mSnapScrollMode = SNAP_NONE;
startTouch(x, y, eventTime);
break;
}
case MotionEvent.ACTION_MOVE: {
@@ -3821,18 +3938,20 @@ public class WebView extends AbsoluteLayout
if (!mDragFromTextInput) {
nativeHideCursor();
}
WebSettings settings = getSettings();
if (settings.supportZoom()
&& settings.getBuiltInZoomControls()
&& !mZoomButtonsController.isVisible()
&& mMinZoomScale < mMaxZoomScale) {
mZoomButtonsController.setVisible(true);
int count = settings.getDoubleTapToastCount();
if (mInZoomOverview && count > 0) {
settings.setDoubleTapToastCount(--count);
Toast.makeText(mContext,
com.android.internal.R.string.double_tap_toast,
Toast.LENGTH_LONG).show();
if (!mSupportMultiTouch) {
WebSettings settings = getSettings();
if (settings.supportZoom()
&& settings.getBuiltInZoomControls()
&& !mZoomButtonsController.isVisible()
&& mMinZoomScale < mMaxZoomScale) {
mZoomButtonsController.setVisible(true);
int count = settings.getDoubleTapToastCount();
if (mInZoomOverview && count > 0) {
settings.setDoubleTapToastCount(--count);
Toast.makeText(mContext,
com.android.internal.R.string.double_tap_toast,
Toast.LENGTH_LONG).show();
}
}
}
}
@@ -3911,7 +4030,8 @@ public class WebView extends AbsoluteLayout
mUserScroll = true;
}
if (!getSettings().getBuiltInZoomControls()) {
if (!mSupportMultiTouch
&& !getSettings().getBuiltInZoomControls()) {
boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
if (mZoomControls != null && showPlusMinus) {
if (mZoomControls.getVisibility() == View.VISIBLE) {
@@ -4010,26 +4130,38 @@ public class WebView extends AbsoluteLayout
break;
}
case MotionEvent.ACTION_CANCEL: {
// we also use mVelocityTracker == null to tell us that we are
// not "moving around", so we can take the slower/prettier
// mode in the drawing code
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
if (mTouchMode == TOUCH_DRAG_MODE) {
WebViewCore.resumeUpdate(mWebViewCore);
}
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
mTouchMode = TOUCH_DONE_MODE;
nativeHideCursor();
cancelTouch();
break;
}
}
return true;
}
private void startTouch(float x, float y, long eventTime) {
mLastTouchX = x;
mLastTouchY = y;
mLastTouchTime = eventTime;
mVelocityTracker = VelocityTracker.obtain();
mSnapScrollMode = SNAP_NONE;
}
private void cancelTouch() {
// we also use mVelocityTracker == null to tell us that we are not
// "moving around", so we can take the slower/prettier mode in the
// drawing code
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
if (mTouchMode == TOUCH_DRAG_MODE) {
WebViewCore.resumeUpdate(mWebViewCore);
}
mPrivateHandler.removeMessages(SWITCH_TO_SHORTPRESS);
mPrivateHandler.removeMessages(SWITCH_TO_LONGPRESS);
mTouchMode = TOUCH_DONE_MODE;
nativeHideCursor();
}
private long mTrackballFirstTime = 0;
private long mTrackballLastTime = 0;
private float mTrackballRemainsX = 0.0f;
@@ -4389,7 +4521,7 @@ public class WebView extends AbsoluteLayout
scale = mDefaultScale;
}
setNewZoomScale(scale, false);
setNewZoomScale(scale, true, false);
if (oldScale != mActualScale) {
// use mZoomPickerScale to see zoom preview first
@@ -4397,9 +4529,6 @@ public class WebView extends AbsoluteLayout
mInvInitialZoomScale = 1.0f / oldScale;
mInvFinalZoomScale = 1.0f / mActualScale;
mZoomScale = mActualScale;
if (!mInZoomOverview) {
mLastScale = scale;
}
invalidate();
return true;
} else {
@@ -4497,18 +4626,13 @@ public class WebView extends AbsoluteLayout
public boolean zoomIn() {
// TODO: alternatively we can disallow this during draw history mode
switchOutDrawHistory();
mInZoomOverview = false;
// Center zooming to the center of the screen.
if (mInZoomOverview) {
// if in overview mode, bring it back to normal mode
mLastTouchX = getViewWidth() * .5f;
mLastTouchY = getViewHeight() * .5f;
doDoubleTap();
return true;
} else {
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
return zoomWithPreview(mActualScale * 1.25f);
}
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
return zoomWithPreview(mActualScale * 1.25f);
}
/**
@@ -4518,18 +4642,12 @@ public class WebView extends AbsoluteLayout
public boolean zoomOut() {
// TODO: alternatively we can disallow this during draw history mode
switchOutDrawHistory();
float scale = mActualScale * 0.8f;
if (scale < (mMinZoomScale + 0.1f)
&& mWebViewCore.getSettings().getUseWideViewPort()) {
// when zoom out to min scale, switch to overview mode
doDoubleTap();
return true;
} else {
// Center zooming to the center of the screen.
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
return zoomWithPreview(scale);
}
// Center zooming to the center of the screen.
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
return zoomWithPreview(mActualScale * 0.8f);
}
private void updateSelection() {
@@ -4645,44 +4763,71 @@ public class WebView extends AbsoluteLayout
}
}
// Rule for double tap:
// 1. if the current scale is not same as the text wrap scale and layout
// algorithm is NARROW_COLUMNS, fit to column;
// 2. if the current state is not overview mode, change to overview mode;
// 3. if the current state is overview mode, change to default scale.
private void doDoubleTap() {
if (mWebViewCore.getSettings().getUseWideViewPort() == false) {
return;
}
mZoomCenterX = mLastTouchX;
mZoomCenterY = mLastTouchY;
mInZoomOverview = !mInZoomOverview;
// remove the zoom control after double tap
mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
WebSettings settings = getSettings();
if (settings.getBuiltInZoomControls()) {
if (mZoomButtonsController.isVisible()) {
mZoomButtonsController.setVisible(false);
}
} else {
if (mZoomControlRunnable != null) {
mPrivateHandler.removeCallbacks(mZoomControlRunnable);
}
if (mZoomControls != null) {
mZoomControls.hide();
if (!mSupportMultiTouch) {
// remove the zoom control after double tap
if (settings.getBuiltInZoomControls()) {
if (mZoomButtonsController.isVisible()) {
mZoomButtonsController.setVisible(false);
}
} else {
if (mZoomControlRunnable != null) {
mPrivateHandler.removeCallbacks(mZoomControlRunnable);
}
if (mZoomControls != null) {
mZoomControls.hide();
}
}
settings.setDoubleTapToastCount(0);
}
settings.setDoubleTapToastCount(0);
if (mInZoomOverview) {
// Force the titlebar fully reveal in overview mode
if (mScrollY < getTitleHeight()) mScrollY = 0;
zoomWithPreview((float) getViewWidth() / mZoomOverviewWidth);
} else {
// mLastTouchX and mLastTouchY are the point in the current viewport
int contentX = viewToContentX((int) mLastTouchX + mScrollX);
int contentY = viewToContentY((int) mLastTouchY + mScrollY);
int left = nativeGetBlockLeftEdge(contentX, contentY, mActualScale);
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);
if ((settings.getLayoutAlgorithm() == WebSettings.LayoutAlgorithm.NARROW_COLUMNS)
&& (Math.abs(mActualScale - mTextWrapScale) >= 0.01f)) {
setNewZoomScale(mActualScale, true, true);
float overviewScale = (float) getViewWidth() / mZoomOverviewWidth;
if (Math.abs(mActualScale - overviewScale) < 0.01f) {
mInZoomOverview = true;
}
zoomWithPreview(mLastScale);
} else if (!mInZoomOverview) {
float newScale = (float) getViewWidth() / mZoomOverviewWidth;
if (Math.abs(mActualScale - newScale) >= 0.01f) {
mInZoomOverview = true;
// Force the titlebar fully reveal in overview mode
if (mScrollY < getTitleHeight()) mScrollY = 0;
zoomWithPreview(newScale);
} else if (Math.abs(mActualScale - mDefaultScale) >= 0.01f) {
mInZoomOverview = true;
}
} else {
mInZoomOverview = false;
int left = nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
if (left != NO_LEFTEDGE) {
// add a 5pt padding to the left edge.
int viewLeft = contentToViewX(left < 5 ? 0 : (left - 5))
- mScrollX;
// Re-calculate the zoom center so that the new scroll x will be
// on the left edge.
if (viewLeft > 0) {
mZoomCenterX = viewLeft * mDefaultScale
/ (mDefaultScale - mActualScale);
} else {
scrollBy(viewLeft, 0);
mZoomCenterX = 0;
}
}
zoomWithPreview(mDefaultScale);
}
}
@@ -4888,9 +5033,10 @@ public class WebView extends AbsoluteLayout
@Override
public void handleMessage(Message msg) {
if (DebugFlags.WEB_VIEW) {
Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
> INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
: HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD
|| msg.what > SHOW_RECT_MSG_ID ? Integer
.toString(msg.what) : HandlerDebugString[msg.what
- REMEMBER_PASSWORD]);
}
if (mWebViewCore == null) {
// after WebView's destroy() is called, skip handling messages.
@@ -4975,9 +5121,6 @@ public class WebView extends AbsoluteLayout
WebViewCore.RestoreState restoreState = draw.mRestoreState;
if (restoreState != null) {
mInZoomOverview = false;
mLastScale = mInitialScaleInPercent > 0
? mInitialScaleInPercent / 100.0f
: restoreState.mTextWrapScale;
if (restoreState.mMinScale == 0) {
if (restoreState.mMobileSite) {
if (draw.mMinPrefWidth >
@@ -4985,6 +5128,8 @@ public class WebView extends AbsoluteLayout
mMinZoomScale = (float) viewWidth
/ draw.mMinPrefWidth;
mMinZoomScaleFixed = false;
mInZoomOverview = useWideViewport &&
settings.getLoadWithOverviewMode();
} else {
mMinZoomScale = restoreState.mDefaultScale;
mMinZoomScaleFixed = true;
@@ -5002,17 +5147,27 @@ public class WebView extends AbsoluteLayout
} else {
mMaxZoomScale = restoreState.mMaxScale;
}
setNewZoomScale(mLastScale, false);
setContentScrollTo(restoreState.mScrollX,
restoreState.mScrollY);
if (useWideViewport
&& settings.getLoadWithOverviewMode()) {
if (restoreState.mViewScale == 0
|| (restoreState.mMobileSite
&& mMinZoomScale < restoreState.mDefaultScale)) {
mInZoomOverview = true;
if (mInitialScaleInPercent > 0) {
setNewZoomScale(mInitialScaleInPercent / 100.0f,
true, false);
} else if (restoreState.mViewScale > 0) {
mTextWrapScale = restoreState.mTextWrapScale;
setNewZoomScale(restoreState.mViewScale, false,
false);
} else {
mInZoomOverview = useWideViewport
&& settings.getLoadWithOverviewMode();
if (mInZoomOverview) {
setNewZoomScale((float) viewWidth
/ WebViewCore.DEFAULT_VIEWPORT_WIDTH,
true, false);
} else {
setNewZoomScale(restoreState.mTextWrapScale,
true, false);
}
}
setContentScrollTo(restoreState.mScrollX,
restoreState.mScrollY);
// As we are on a new page, remove the WebTextView. This
// is necessary for page loads driven by webkit, and in
// particular when the user was on a password field, so
@@ -5050,7 +5205,7 @@ public class WebView extends AbsoluteLayout
if (Math.abs((viewWidth * mInvActualScale)
- mZoomOverviewWidth) > 1) {
setNewZoomScale((float) viewWidth
/ mZoomOverviewWidth, false);
/ mZoomOverviewWidth, true, false);
}
}
break;
@@ -5182,6 +5337,43 @@ public class WebView extends AbsoluteLayout
}
break;
case SHOW_RECT_MSG_ID:
WebViewCore.ShowRectData data = (WebViewCore.ShowRectData) msg.obj;
int x = mScrollX;
int left = contentToViewDimension(data.mLeft);
int width = contentToViewDimension(data.mWidth);
int maxWidth = contentToViewDimension(data.mContentWidth);
int viewWidth = getViewWidth();
if (width < viewWidth) {
// center align
x += left + width / 2 - mScrollX - viewWidth / 2;
} else {
x += (int) (left + data.mXPercentInDoc * width
- mScrollX - data.mXPercentInView * viewWidth);
}
// use the passing content width to cap x as the current
// mContentWidth may not be updated yet
x = Math.max(0,
(Math.min(maxWidth, x + viewWidth)) - viewWidth);
int y = mScrollY;
int top = contentToViewDimension(data.mTop);
int height = contentToViewDimension(data.mHeight);
int maxHeight = contentToViewDimension(data.mContentHeight);
int viewHeight = getViewHeight();
if (height < viewHeight) {
// middle align
y += top + height / 2 - mScrollY - viewHeight / 2;
} else {
y += (int) (top + data.mYPercentInDoc * height
- mScrollY - data.mYPercentInView * viewHeight);
}
// use the passing content height to cap y as the current
// mContentHeight may not be updated yet
y = Math.max(0,
(Math.min(maxHeight, y + viewHeight) - viewHeight));
scrollTo(x, y);
break;
default:
super.handleMessage(msg);
break;

View File

@@ -447,8 +447,8 @@ final class WebViewCore {
should this be called nativeSetViewPortSize?
*/
private native void nativeSetSize(int width, int height, int screenWidth,
float scale, int realScreenWidth, int screenHeight,
boolean ignoreHeight);
float scale, int realScreenWidth, int screenHeight, int anchorX,
int anchorY, boolean ignoreHeight);
private native int nativeGetContentMinPrefWidth();
@@ -961,6 +961,7 @@ final class WebViewCore {
(WebView.ViewSizeData) msg.obj;
viewSizeChanged(data.mWidth, data.mHeight,
data.mTextWrapWidth, data.mScale,
data.mAnchorX, data.mAnchorY,
data.mIgnoreHeight);
break;
}
@@ -1483,7 +1484,7 @@ final class WebViewCore {
// notify webkit that our virtual view size changed size (after inv-zoom)
private void viewSizeChanged(int w, int h, int textwrapWidth, float scale,
boolean ignoreHeight) {
int anchorX, int anchorY, boolean ignoreHeight) {
if (DebugFlags.WEB_VIEW_CORE) {
Log.v(LOGTAG, "viewSizeChanged w=" + w + "; h=" + h
+ "; textwrapWidth=" + textwrapWidth + "; scale=" + scale);
@@ -1514,12 +1515,14 @@ final class WebViewCore {
width = Math.max(w, Math.max(DEFAULT_VIEWPORT_WIDTH,
nativeGetContentMinPrefWidth()));
}
} else {
} else if (mViewportWidth > 0) {
width = Math.max(w, mViewportWidth);
} else {
width = Math.max(w, nativeGetContentMinPrefWidth());
}
}
nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
textwrapWidth, scale, w, h, ignoreHeight);
textwrapWidth, scale, w, h, anchorX, anchorY, ignoreHeight);
// Remember the current width and height
boolean needInvalidate = (mCurrentViewWidth == 0);
mCurrentViewWidth = w;
@@ -1953,14 +1956,12 @@ final class WebViewCore {
mRestoreState.mScrollY = mRestoredY;
mRestoreState.mMobileSite = (0 == mViewportWidth);
if (mRestoredScale > 0) {
mRestoreState.mViewScale = mRestoredScale / 100.0f;
if (mRestoredScreenWidthScale > 0) {
mRestoreState.mTextWrapScale =
mRestoredScreenWidthScale / 100.0f;
// 0 will trigger WebView to turn on zoom overview mode
mRestoreState.mViewScale = 0;
} else {
mRestoreState.mViewScale = mRestoreState.mTextWrapScale =
mRestoredScale / 100.0f;
mRestoreState.mTextWrapScale = mRestoreState.mViewScale;
}
} else {
if (mViewportInitialScale > 0) {
@@ -1993,6 +1994,7 @@ final class WebViewCore {
data.mTextWrapWidth = data.mWidth;
data.mScale = -1.0f;
data.mIgnoreHeight = false;
data.mAnchorX = data.mAnchorY = 0;
// send VIEW_SIZE_CHANGED to the front of the queue so that we can
// avoid pushing the wrong picture to the WebView side. If there is
// a VIEW_SIZE_CHANGED in the queue, probably from WebView side,
@@ -2021,6 +2023,7 @@ final class WebViewCore {
data.mTextWrapWidth = Math.round(webViewWidth
/ mRestoreState.mTextWrapScale);
data.mIgnoreHeight = false;
data.mAnchorX = data.mAnchorY = 0;
// send VIEW_SIZE_CHANGED to the front of the queue so that we
// can avoid pushing the wrong picture to the WebView side.
mEventHub.removeMessages(EventHub.VIEW_SIZE_CHANGED);
@@ -2177,6 +2180,40 @@ final class WebViewCore {
childView.removeView();
}
// called by JNI
static class ShowRectData {
int mLeft;
int mTop;
int mWidth;
int mHeight;
int mContentWidth;
int mContentHeight;
float mXPercentInDoc;
float mXPercentInView;
float mYPercentInDoc;
float mYPercentInView;
}
private void showRect(int left, int top, int width, int height,
int contentWidth, int contentHeight, float xPercentInDoc,
float xPercentInView, float yPercentInDoc, float yPercentInView) {
if (mWebView != null) {
ShowRectData data = new ShowRectData();
data.mLeft = left;
data.mTop = top;
data.mWidth = width;
data.mHeight = height;
data.mContentWidth = contentWidth;
data.mContentHeight = contentHeight;
data.mXPercentInDoc = xPercentInDoc;
data.mXPercentInView = xPercentInView;
data.mYPercentInDoc = yPercentInDoc;
data.mYPercentInView = yPercentInView;
Message.obtain(mWebView.mPrivateHandler, WebView.SHOW_RECT_MSG_ID,
data).sendToTarget();
}
}
private native void nativePause();
private native void nativeResume();
private native void nativeFreeMemory();