summaryrefslogtreecommitdiffstats
path: root/core/java/android/webkit/WebView.java
diff options
context:
space:
mode:
Diffstat (limited to 'core/java/android/webkit/WebView.java')
-rw-r--r--core/java/android/webkit/WebView.java294
1 files changed, 260 insertions, 34 deletions
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index bdbf38a..4d9a8fb 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -45,6 +45,8 @@ import android.util.AttributeSet;
import android.util.Config;
import android.util.Log;
import android.view.animation.AlphaAnimation;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
@@ -234,6 +236,9 @@ public class WebView extends AbsoluteLayout
*/
VelocityTracker mVelocityTracker;
+ private static boolean mShowZoomRingTutorial = true;
+ private static final int ZOOM_RING_TUTORIAL_DURATION = 3000;
+
/**
* Touch mode
*/
@@ -284,9 +289,7 @@ public class WebView extends AbsoluteLayout
// In the browser, if it switches out of tap too soon, jump tap won't work.
private static final int TAP_TIMEOUT = 200;
// The duration in milliseconds we will wait to see if it is a double tap.
- // With a limited survey, the time between the first tap up and the second
- // tap down in the double tap case is around 70ms - 120ms.
- private static final int DOUBLE_TAP_TIMEOUT = 200;
+ private static final int DOUBLE_TAP_TIMEOUT = 250;
// This should be ViewConfiguration.getLongPressTimeout()
// But system time out is 500ms, which is too short for the browser.
// With a short timeout, it's difficult to treat trigger a short press.
@@ -315,6 +318,9 @@ public class WebView extends AbsoluteLayout
private int mContentWidth; // cache of value from WebViewCore
private int mContentHeight; // cache of value from WebViewCore
+ static int MAX_FLOAT_CONTENT_WIDTH = 480;
+ private int mMinContentWidth;
+
// Need to have the separate control for horizontal and vertical scrollbar
// style than the View's single scrollbar style
private boolean mOverlayHorizontalScrollbar = true;
@@ -348,6 +354,7 @@ public class WebView extends AbsoluteLayout
private static final int UPDATE_TEXT_ENTRY_ADAPTER = 6;
private static final int SWITCH_TO_ENTER = 7;
private static final int RESUME_WEBCORE_UPDATE = 8;
+ private static final int DISMISS_ZOOM_RING_TUTORIAL = 9;
//! arg1=x, arg2=y
static final int SCROLL_TO_MSG_ID = 10;
@@ -370,12 +377,46 @@ public class WebView extends AbsoluteLayout
static final int WEBCORE_NEED_TOUCH_EVENTS = 25;
// obj=Rect in doc coordinates
static final int INVAL_RECT_MSG_ID = 26;
+
+ static final String[] HandlerDebugString = {
+ "REMEMBER_PASSWORD", // = 1;
+ "NEVER_REMEMBER_PASSWORD", // = 2;
+ "SWITCH_TO_SHORTPRESS", // = 3;
+ "SWITCH_TO_LONGPRESS", // = 4;
+ "RELEASE_SINGLE_TAP", // = 5;
+ "UPDATE_TEXT_ENTRY_ADAPTER", // = 6;
+ "SWITCH_TO_ENTER", // = 7;
+ "RESUME_WEBCORE_UPDATE", // = 8;
+ "9",
+ "SCROLL_TO_MSG_ID", // = 10;
+ "SCROLL_BY_MSG_ID", // = 11;
+ "SPAWN_SCROLL_TO_MSG_ID", // = 12;
+ "SYNC_SCROLL_TO_MSG_ID", // = 13;
+ "NEW_PICTURE_MSG_ID", // = 14;
+ "UPDATE_TEXT_ENTRY_MSG_ID", // = 15;
+ "WEBCORE_INITIALIZED_MSG_ID", // = 16;
+ "UPDATE_TEXTFIELD_TEXT_MSG_ID", // = 17;
+ "DID_FIRST_LAYOUT_MSG_ID", // = 18;
+ "RECOMPUTE_FOCUS_MSG_ID", // = 19;
+ "NOTIFY_FOCUS_SET_MSG_ID", // = 20;
+ "MARK_NODE_INVALID_ID", // = 21;
+ "UPDATE_CLIPBOARD", // = 22;
+ "LONG_PRESS_ENTER", // = 23;
+ "PREVENT_TOUCH_ID", // = 24;
+ "WEBCORE_NEED_TOUCH_EVENTS", // = 25;
+ "INVAL_RECT_MSG_ID" // = 26;
+ };
// width which view is considered to be fully zoomed out
static final int ZOOM_OUT_WIDTH = 1024;
- private static final float DEFAULT_MAX_ZOOM_SCALE = 4;
- private static final float DEFAULT_MIN_ZOOM_SCALE = 0.25f;
+ private static final float MAX_ZOOM_RING_ANGLE = (float) (Math.PI * 2 / 3);
+ private static final int ZOOM_RING_STEPS = 4;
+ private static final float ZOOM_RING_ANGLE_UNIT = MAX_ZOOM_RING_ANGLE
+ / ZOOM_RING_STEPS;
+
+ private static final float DEFAULT_MAX_ZOOM_SCALE = 2;
+ private static final float DEFAULT_MIN_ZOOM_SCALE = (float) 1/3;
// scale limit, which can be set through viewport meta tag in the web page
private float mMaxZoomScale = DEFAULT_MAX_ZOOM_SCALE;
private float mMinZoomScale = DEFAULT_MIN_ZOOM_SCALE;
@@ -505,6 +546,8 @@ public class WebView extends AbsoluteLayout
}
private ZoomRingController mZoomRingController;
+ private ImageView mZoomRingOverview;
+ private Animation mZoomRingOverviewExitAnimation;
// These keep track of the center point of the zoom ring. They are used to
// determine the point around which we should zoom.
@@ -519,51 +562,83 @@ public class WebView extends AbsoluteLayout
// in this callback
}
+ public void onBeginPan() {
+ setZoomOverviewVisible(false);
+ }
+
public boolean onPan(int deltaX, int deltaY) {
return pinScrollBy(deltaX, deltaY, false, 0);
}
+ public void onEndPan() {
+ }
+
public void onVisibilityChanged(boolean visible) {
if (visible) {
- mZoomControls.show(false, canZoomScrollOut());
- } else {
- mZoomControls.hide();
+ switchOutDrawHistory();
+ float angle = 0f;
+ if (mActualScale > 1) {
+ angle = -(float) Math.round(ZOOM_RING_STEPS
+ * (mActualScale - 1) / (mMaxZoomScale - 1))
+ / ZOOM_RING_STEPS;
+ } else if (mActualScale < 1) {
+ angle = (float) Math.round(ZOOM_RING_STEPS
+ * (1 - mActualScale) / (1 - mMinZoomScale))
+ / ZOOM_RING_STEPS;
+ }
+ mZoomRingController.setThumbAngle(angle * MAX_ZOOM_RING_ANGLE);
+
+ // Show the zoom overview tab on the ring
+ setZoomOverviewVisible(true);
}
}
-
- public void onBeginDrag(float startAngle) {
+
+ public void onBeginDrag() {
mPreviewZoomOnly = true;
+ setZoomOverviewVisible(false);
}
- public void onEndDrag(float endAngle) {
+ public void onEndDrag() {
mPreviewZoomOnly = false;
setNewZoomScale(mActualScale, true);
}
public boolean onDragZoom(int deltaZoomLevel, int centerX,
int centerY, float startAngle, float curAngle) {
-
- if (mZoomScale == mMinZoomScale && deltaZoomLevel < 0 ||
- mZoomScale == mMaxZoomScale && deltaZoomLevel > 0 ||
- deltaZoomLevel == 0) {
+ if (deltaZoomLevel < 0
+ && Math.abs(mActualScale - mMinZoomScale) < 0.01f
+ || deltaZoomLevel > 0
+ && Math.abs(mActualScale - mMaxZoomScale) < 0.01f
+ || deltaZoomLevel == 0) {
return false;
}
mZoomCenterX = (float) centerX;
mZoomCenterY = (float) centerY;
- while (deltaZoomLevel != 0) {
- if (deltaZoomLevel > 0) {
- if (!zoomIn()) return false;
- deltaZoomLevel--;
+ float scale = 1.0f;
+ if (curAngle > (float) Math.PI)
+ curAngle -= (float) 2 * Math.PI;
+ if (curAngle > 0) {
+ if (curAngle >= MAX_ZOOM_RING_ANGLE) {
+ scale = mMinZoomScale;
+ } else {
+ scale = 1 - (float) Math.round(curAngle
+ / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS
+ * (1 - mMinZoomScale);
+ }
+ } else if (curAngle < 0) {
+ if (curAngle <= -MAX_ZOOM_RING_ANGLE) {
+ scale = mMaxZoomScale;
} else {
- if (!zoomOut()) return false;
- deltaZoomLevel++;
+ scale = 1 + (float) Math.round(-curAngle
+ / ZOOM_RING_ANGLE_UNIT) / ZOOM_RING_STEPS
+ * (mMaxZoomScale - 1);
}
}
-
+ zoomWithPreview(scale);
return true;
}
-
+
public void onSimpleZoom(boolean zoomIn) {
if (zoomIn) {
zoomIn();
@@ -571,6 +646,7 @@ public class WebView extends AbsoluteLayout
zoomOut();
}
}
+
};
/**
@@ -610,7 +686,14 @@ public class WebView extends AbsoluteLayout
mFocusData.mY = 0;
mScroller = new Scroller(context);
mZoomRingController = new ZoomRingController(context, this);
+ mZoomRingController.setResetThumbAutomatically(false);
+ mZoomRingController.setThumbClockwiseBound(
+ (float) (2 * Math.PI - MAX_ZOOM_RING_ANGLE));
+ mZoomRingController.setThumbCounterclockwiseBound(MAX_ZOOM_RING_ANGLE);
mZoomRingController.setCallback(mZoomListener);
+ mZoomRingController.setZoomRingTrack(
+ com.android.internal.R.drawable.zoom_ring_track_absolute);
+ createZoomRingOverviewTab();
}
private void init() {
@@ -625,6 +708,63 @@ public class WebView extends AbsoluteLayout
mMinLockSnapReverseDistance = slop;
}
+ private void createZoomRingOverviewTab() {
+ Context context = getContext();
+
+ mZoomRingOverviewExitAnimation = AnimationUtils.loadAnimation(context,
+ com.android.internal.R.anim.fade_out);
+
+ mZoomRingOverview = new ImageView(context);
+ mZoomRingOverview.setBackgroundResource(
+ com.android.internal.R.drawable.zoom_ring_overview_tab);
+ mZoomRingOverview.setImageResource(com.android.internal.R.drawable.btn_zoom_page);
+
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ FrameLayout.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER);
+ // TODO: magic constant that's based on the zoom ring radius + some offset
+ lp.topMargin = 208;
+ mZoomRingOverview.setLayoutParams(lp);
+ mZoomRingOverview.setOnClickListener(new View.OnClickListener() {
+ public void onClick(View v) {
+ // Hide the zoom ring
+ mZoomRingController.setVisible(false);
+ zoomScrollOut();
+ }});
+
+ // Measure the overview View to figure out its height
+ mZoomRingOverview.forceLayout();
+ mZoomRingOverview.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
+ MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
+
+ ViewGroup container = mZoomRingController.getContainer();
+ // Find the index of the zoom ring in the container
+ View zoomRing = container.findViewById(mZoomRingController.getZoomRingId());
+ int zoomRingIndex;
+ for (zoomRingIndex = container.getChildCount() - 1; zoomRingIndex >= 0; zoomRingIndex--) {
+ if (container.getChildAt(zoomRingIndex) == zoomRing) break;
+ }
+ // Add the overview tab below the zoom ring (so we don't steal its events)
+ container.addView(mZoomRingOverview, zoomRingIndex);
+ // Since we use margins to adjust the vertical placement of the tab, the widget
+ // ends up getting clipped off. Ensure the container is big enough for
+ // us.
+ int myHeight = mZoomRingOverview.getMeasuredHeight() + lp.topMargin / 2;
+ // Multiplied by 2 b/c the zoom ring needs to be centered on the screen
+ container.setMinimumHeight(myHeight * 2);
+ }
+
+ private void setZoomOverviewVisible(boolean visible) {
+ int newVisibility = visible ? View.VISIBLE : View.INVISIBLE;
+ if (mZoomRingOverview.getVisibility() == newVisibility) return;
+
+ if (!visible) {
+ mZoomRingOverview.startAnimation(mZoomRingOverviewExitAnimation);
+ }
+ mZoomRingOverview.setVisibility(newVisibility);
+ }
+
/* package */ boolean onSavePassword(String schemePlusHost, String username,
String password, final Message resumeMsg) {
boolean rVal = false;
@@ -1653,7 +1793,8 @@ public class WebView extends AbsoluteLayout
* @return true if new values were sent
*/
private boolean sendViewSizeZoom() {
- int newWidth = Math.round(getViewWidth() * mInvActualScale);
+ int viewWidth = getViewWidth();
+ int newWidth = Math.round(viewWidth * mInvActualScale);
int newHeight = Math.round(getViewHeight() * mInvActualScale);
/*
* Because the native side may have already done a layout before the
@@ -1669,7 +1810,7 @@ public class WebView extends AbsoluteLayout
// Avoid sending another message if the dimensions have not changed.
if (newWidth != mLastWidthSent || newHeight != mLastHeightSent) {
mWebViewCore.sendMessage(EventHub.VIEW_SIZE_CHANGED,
- newWidth, newHeight, new Float(mActualScale));
+ newWidth, newHeight, new Integer(viewWidth));
mLastWidthSent = newWidth;
mLastHeightSent = newHeight;
return true;
@@ -1968,7 +2109,7 @@ public class WebView extends AbsoluteLayout
// Scale from content to view coordinates, and pin.
// Also called by jni webview.cpp
- private void setContentScrollBy(int cx, int cy) {
+ private void setContentScrollBy(int cx, int cy, boolean animate) {
if (mDrawHistory) {
// disallow WebView to change the scroll position as History Picture
// is used in the view system.
@@ -1992,10 +2133,10 @@ public class WebView extends AbsoluteLayout
// vertical scroll?
// Log.d(LOGTAG, "setContentScrollBy cy=" + cy);
if (cy == 0 && cx != 0) {
- pinScrollBy(cx, 0, true, 0);
+ pinScrollBy(cx, 0, animate, 0);
}
} else {
- pinScrollBy(cx, cy, true, 0);
+ pinScrollBy(cx, cy, animate, 0);
}
}
@@ -2205,7 +2346,8 @@ public class WebView extends AbsoluteLayout
// state.
// If mNativeClass is 0, we should not reach here, so we do not
// need to check it again.
- nativeRecordButtons(mTouchMode == TOUCH_SHORTPRESS_START_MODE
+ nativeRecordButtons(hasFocus() && hasWindowFocus(),
+ mTouchMode == TOUCH_SHORTPRESS_START_MODE
|| mTrackballDown || mGotEnterDown, false);
drawCoreAndFocusRing(canvas, mBackgroundColor, mDrawFocusRing);
}
@@ -2254,6 +2396,8 @@ public class WebView extends AbsoluteLayout
invalidate();
} else {
zoomScale = mZoomScale;
+ // set mZoomScale to be 0 as we have done animation
+ mZoomScale = 0;
}
float scale = (mActualScale - zoomScale) * mInvActualScale;
float tx = scale * (mZoomCenterX + mScrollX);
@@ -2775,6 +2919,17 @@ public class WebView extends AbsoluteLayout
getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(mTextEntry, 0);
mTextEntry.enableScrollOnScreen(true);
+ // Now we need to fake a touch event to place the cursor where the
+ // user touched.
+ AbsoluteLayout.LayoutParams lp = (AbsoluteLayout.LayoutParams)
+ mTextEntry.getLayoutParams();
+ if (lp != null) {
+ // Take the last touch and adjust for the location of the
+ // TextDialog.
+ float x = mLastTouchX - lp.x;
+ float y = mLastTouchY - lp.y;
+ mTextEntry.fakeTouchEvent(x, y);
+ }
}
private void updateTextEntry() {
@@ -2987,7 +3142,9 @@ public class WebView extends AbsoluteLayout
mGotEnterDown = true;
mPrivateHandler.sendMessageDelayed(mPrivateHandler
.obtainMessage(LONG_PRESS_ENTER), LONG_PRESS_TIMEOUT);
- nativeRecordButtons(true, true);
+ // Already checked mNativeClass, so we do not need to check it
+ // again.
+ nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
return true;
}
// Bubble up the key event as WebView doesn't handle it
@@ -3198,6 +3355,9 @@ public class WebView extends AbsoluteLayout
ViewGroup p = (ViewGroup) parent;
p.setOnHierarchyChangeListener(null);
}
+
+ // Clean up the zoom ring
+ mZoomRingController.setVisible(false);
}
// Implementation for OnHierarchyChangeListener
@@ -3234,16 +3394,25 @@ public class WebView extends AbsoluteLayout
if (mNeedsUpdateTextEntry) {
updateTextEntry();
}
+ if (mNativeClass != 0) {
+ nativeRecordButtons(true, false, true);
+ }
} else {
// If our window gained focus, but we do not have it, do not
// draw the focus ring.
mDrawFocusRing = false;
+ // We do not call nativeRecordButtons here because we assume
+ // that when we lost focus, or window focus, it got called with
+ // false for the first parameter
}
} else {
// If our window has lost focus, stop drawing the focus ring
mDrawFocusRing = false;
mGotKeyDown = false;
mShiftIsPressed = false;
+ if (mNativeClass != 0) {
+ nativeRecordButtons(false, false, true);
+ }
}
invalidate();
super.onWindowFocusChanged(hasWindowFocus);
@@ -3264,12 +3433,22 @@ public class WebView extends AbsoluteLayout
updateTextEntry();
mNeedsUpdateTextEntry = false;
}
+ if (mNativeClass != 0) {
+ nativeRecordButtons(true, false, true);
+ }
+ //} else {
+ // The WebView has gained focus while we do not have
+ // windowfocus. When our window lost focus, we should have
+ // called nativeRecordButtons(false...)
}
} else {
// When we lost focus, unless focus went to the TextView (which is
// true if we are in editing mode), stop drawing the focus ring.
if (!inEditingMode()) {
mDrawFocusRing = false;
+ if (mNativeClass != 0) {
+ nativeRecordButtons(false, false, true);
+ }
}
mGotKeyDown = false;
}
@@ -3285,6 +3464,22 @@ public class WebView extends AbsoluteLayout
// the new zoom ring controller
mZoomCenterX = getViewWidth() * .5f;
mZoomCenterY = getViewHeight() * .5f;
+
+ // update mMinZoomScale
+ if (mMinContentWidth > MAX_FLOAT_CONTENT_WIDTH) {
+ boolean atMin = Math.abs(mActualScale - mMinZoomScale) < 0.01f;
+ mMinZoomScale = (float) getViewWidth() / mMinContentWidth;
+ if (atMin) {
+ // if the WebView was at the minimum zoom scale, keep it. e,g.,
+ // the WebView was at the minimum zoom scale at the portrait
+ // mode, rotate it to the landscape modifying the scale to the
+ // new minimum zoom scale, when rotating back, we would like to
+ // keep the minimum zoom scale instead of keeping the same scale
+ // as normally we do.
+ mActualScale = mMinZoomScale;
+ }
+ }
+
// we always force, in case our height changed, in which case we still
// want to send the notification over to webkit
setNewZoomScale(mActualScale, true);
@@ -3340,6 +3535,14 @@ public class WebView extends AbsoluteLayout
return false;
}
+ if (mShowZoomRingTutorial && mMinZoomScale < mMaxZoomScale) {
+ ZoomRingController.showZoomTutorialOnce(mContext);
+ mShowZoomRingTutorial = false;
+ mPrivateHandler.sendMessageDelayed(mPrivateHandler
+ .obtainMessage(DISMISS_ZOOM_RING_TUTORIAL),
+ ZOOM_RING_TUTORIAL_DURATION);
+ }
+
if (LOGV_ENABLED) {
Log.v(LOGTAG, ev + " at " + ev.getEventTime() + " mTouchMode="
+ mTouchMode);
@@ -3749,7 +3952,7 @@ public class WebView extends AbsoluteLayout
mPrivateHandler.removeMessages(SWITCH_TO_ENTER);
mTrackballDown = true;
if (mNativeClass != 0) {
- nativeRecordButtons(true, true);
+ nativeRecordButtons(hasFocus() && hasWindowFocus(), true, true);
}
if (time - mLastFocusTime <= TRACKBALL_TIMEOUT
&& !mLastFocusBounds.equals(nativeGetFocusRingBounds())) {
@@ -4148,6 +4351,9 @@ public class WebView extends AbsoluteLayout
});
zoomControls.setOnZoomMagnifyClickListener(new OnClickListener() {
public void onClick(View v) {
+ // Hide the zoom ring
+ mZoomRingController.setVisible(false);
+
mPrivateHandler.removeCallbacks(mZoomControlRunnable);
mPrivateHandler.postDelayed(mZoomControlRunnable,
ZOOM_CONTROLS_TIMEOUT);
@@ -4200,6 +4406,12 @@ public class WebView extends AbsoluteLayout
}
}
+ // Called by JNI to handle a touch on a node representing an email address,
+ // address, or phone number
+ private void overrideLoading(String url) {
+ mCallbackProxy.uiOverrideUrlLoading(url);
+ }
+
@Override
public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
boolean result = false;
@@ -4372,6 +4584,11 @@ public class WebView extends AbsoluteLayout
class PrivateHandler extends Handler {
@Override
public void handleMessage(Message msg) {
+ if (LOGV_ENABLED) {
+ Log.v(LOGTAG, msg.what < REMEMBER_PASSWORD || msg.what
+ > INVAL_RECT_MSG_ID ? Integer.toString(msg.what)
+ : HandlerDebugString[msg.what - REMEMBER_PASSWORD]);
+ }
switch (msg.what) {
case REMEMBER_PASSWORD: {
mDatabase.setUsernamePassword(
@@ -4413,7 +4630,7 @@ public class WebView extends AbsoluteLayout
, KeyEvent.KEYCODE_ENTER));
break;
case SCROLL_BY_MSG_ID:
- setContentScrollBy(msg.arg1, msg.arg2);
+ setContentScrollBy(msg.arg1, msg.arg2, (Boolean) msg.obj);
break;
case SYNC_SCROLL_TO_MSG_ID:
if (mUserScroll) {
@@ -4451,6 +4668,11 @@ public class WebView extends AbsoluteLayout
0, 0);
}
}
+ mMinContentWidth = msg.arg1;
+ if (mMinContentWidth > MAX_FLOAT_CONTENT_WIDTH) {
+ mMinZoomScale = (float) getViewWidth()
+ / mMinContentWidth;
+ }
// 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
@@ -4638,6 +4860,10 @@ public class WebView extends AbsoluteLayout
}
break;
+ case DISMISS_ZOOM_RING_TUTORIAL:
+ mZoomRingController.finishZoomTutorial();
+ break;
+
default:
super.handleMessage(msg);
break;
@@ -5018,8 +5244,8 @@ public class WebView extends AbsoluteLayout
private native void nativeRecomputeFocus();
// Like many other of our native methods, you must make sure that
// mNativeClass is not null before calling this method.
- private native void nativeRecordButtons(boolean pressed,
- boolean invalidate);
+ private native void nativeRecordButtons(boolean focused,
+ boolean pressed, boolean invalidate);
private native void nativeResetFocus();
private native void nativeResetNavClipBounds();
private native void nativeSelectBestAt(Rect rect);